public void doError(
     HttpServletRequest request,
     HttpServletResponse response,
     Map<String, Object> theMap,
     String s,
     String message,
     Exception e)
     throws java.io.IOException {
   if (e != null) {
     M_log.error(e.getLocalizedMessage(), e);
   }
   theMap.put("/message_response/statusinfo/codemajor", "Fail");
   theMap.put("/message_response/statusinfo/severity", "Error");
   String msg = rb.getString(s) + ": " + message;
   M_log.info(msg);
   theMap.put(
       "/message_response/statusinfo/description", FormattedText.escapeHtmlFormattedText(msg));
   String theXml = XMLMap.getXML(theMap, true);
   PrintWriter out = response.getWriter();
   out.println(theXml);
   M_log.info("doError=" + theXml);
 }
Example #2
0
  public static boolean unitTest(String xmlString, boolean doDebug) {

    if (xmlString == null) return false;
    debugFlag = doDebug;

    // If Debug is turned on - let the chips fly, exceptions and
    // All...
    if (doDebug) {
      debugFlag = true;
      String pretty1 = XMLMap.prettyPrint(xmlString);
      String pretty2 = XMLMap.prettyPrint(pretty1);
      if (pretty1.equals(pretty2)) return true;
      System.out.println("XMLMap - unit test failed");
      return false;
    }

    // For Debug off - we first try it silently and in a try/catch block
    debugFlag = false;
    try {
      String pretty1 = XMLMap.prettyPrint(xmlString);
      String pretty2 = XMLMap.prettyPrint(pretty1);
      if (pretty1.equals(pretty2)) return true;
    } catch (Throwable t) {
      // We will re-do below so folks see the trace back -
      // in the context of debug
    }

    // If we failed - re-do it with verbose mode on
    System.out.println("XMLMap - unit test failed");
    System.out.println(xmlString);
    debugFlag = true;
    String pretty1 = XMLMap.prettyPrint(xmlString);
    System.out.println("Pretty Print Version pass 1\n" + pretty1);
    String pretty2 = XMLMap.prettyPrint(pretty1);
    System.out.println("Pretty Print Version pass 2\n" + pretty2);
    debugFlag = false; // Always reset class-wide variable
    return false;
  }
Example #3
0
 public static List<Map<String, Object>> getList(String xmlInput, String key) {
   Map<String, Object> tmpMap = XMLMap.getFullMap(xmlInput);
   return XMLMap.getList(tmpMap, key);
 }
Example #4
0
 // This process a pretty print from an input string -
 // It does it the hard way - using the methods in this class.
 // It may not be the ideal way to pretty print a XML String
 // but it is our way and we want to be D.R.Y. here...
 // As such you may see some error messages from
 // the XMLMap class in the pretty printing.
 public static String prettyPrint(String input) {
   Map<String, Object> theMap = XMLMap.getFullMap(input);
   return XMLMap.getXML(theMap, true);
 }
Example #5
0
  public static void runSamples() {
    System.out.println("Running XMLMap (www.mdom.org) Samples...");
    debugFlag = false;

    // Test the parsing of a Basic string Map
    Map<String, String> tm = XMLMap.getMap(simpleText);
    // System.out.println("tm="+tm);

    // Test the production of a basic map
    Map<String, String> simpleMap = new TreeMap<String, String>();
    simpleMap.put("/a/b!x", "X");
    simpleMap.put("/a/b", "B");
    simpleMap.put("/a/c/d", "D");
    System.out.println("simpleMap\n" + simpleMap);
    String simpleXml = XMLMap.getXML(simpleMap, true);
    System.out.println("simpleXml\n" + simpleXml);
    unitTest(simpleXml, false);

    // Do a select of a subMap
    Map<String, String> subMap = XMLMap.selectSubMap(tm, "/a/c");
    Map<String, Object> joinedMap = new TreeMap<String, Object>();
    System.out.println("subMap=" + subMap);
    joinedMap.put("/top/id", "1234");
    joinedMap.put("/top/fun", subMap); // Graft the map onto this node
    System.out.println("joinedMap\n" + joinedMap);
    String joinedXml = XMLMap.getXML(joinedMap, true);
    System.out.println("joinedXML\n" + joinedXml);
    unitTest(joinedXml, false);

    // Do an Array
    Map<String, Object> arrayMap = new TreeMap<String, Object>();
    String[] arrayStr = {"first", "second", "third"};
    arrayMap.put("/root/stuff", arrayStr);
    System.out.println("arrayMap\n" + arrayMap);
    String arrayXml = XMLMap.getXML(arrayMap, true);
    System.out.println("arrayXml\n" + arrayXml);
    unitTest(arrayXml, false);

    // Make a Map that is a combination of Maps, String, and Arrays
    Map<String, Object> newMap = new TreeMap<String, Object>();

    newMap.put("/Root/milton", "Root-milton");
    newMap.put("/Root/joe", "Root-joe");

    Map<String, String> m2 = new TreeMap<String, String>();
    m2.put("/fred/a", "fred-a");
    m2.put("/fred/b", "fred-b");
    newMap.put("/Root/freds", m2);

    // Add a list of maps
    // <Root>
    //   <maps>
    //     <map>
    //       <key>key-0</key>
    //       <val>val-0</val>
    //     </map>
    //     <map>
    //       <key>key-1</key>
    //       <val>val-1</val>
    //     </map>
    //   </maps>
    // </Root>

    List<Map<String, String>> lm = new ArrayList<Map<String, String>>();
    Map<String, String> m3 = null;
    m3 = new TreeMap<String, String>();
    m3.put("/key", "key-0");
    m3.put("/val", "val-0");
    lm.add(m3);

    m3 = new TreeMap<String, String>();
    m3.put("/key", "key-1");
    m3.put("/val", "val-1");
    lm.add(m3);

    newMap.put("/Root/maps/map", lm);

    // Add an array of Strings
    // <Root>
    //   <array>first</array>
    //   <array>second</array>
    //   <array>third</array>
    // </Root>

    String[] strar = {"first", "second", "third"};
    newMap.put("/Root/array", strar);

    // Add a list of Maps - this is a bit of a weird application - mostly as a
    // completeness test to insure lists of maps and arrays are equivalent.  Also
    // since the getFullMap returns maps, not Arrays of strings, this is necessary
    // to insure symmetry - i.e. we can take a map structure we produce and
    // regenerate the XML.  Most users will not use this form in construction.
    //
    // <Root>
    //     <item>item-1</item>
    //     <item>item-2</item>
    // </Root>

    List<Map<String, String>> l1 = new ArrayList<Map<String, String>>();
    Map<String, String> m4 = new TreeMap<String, String>();
    m4.put("/", "item-1");
    l1.add(m4);
    Map<String, String> m5 = new TreeMap<String, String>();
    m5.put("/", "item-2");
    l1.add(m5);
    newMap.put("/Root/item", l1);

    // Put in using the XMLMap bracket Syntax - not a particularly good
    // Way to represent multiple items - it is just here for completeness.
    newMap.put("/Root/anns/ann[0]", "Root-ann[0]");
    newMap.put("/Root/anns/ann[1]", "Root-ann[1]");
    newMap.put("/Root/bobs/bob[0]/key", "Root-bobs-bob[0]-key");
    newMap.put("/Root/bobs/bob[0]/val", "Root-bobs-bob[0]-val");
    newMap.put("/Root/bobs/bob[1]/key", "Root-bobs-bob[1]-key");
    newMap.put("/Root/bobs/bob[1]/val", "Root-bobs-bob[1]-val");

    // This is not allowed because maps cannot have duplicates
    /*
          Map<String,String> m6 = new TreeMap<String,String>();
          m5.put("/two", "two-1");
          m5.put("/two", "two-2");
          newMap.put("/Root", m6);
    */

    // Take the Map - turn it into XML and then parse the returned
    // XML into a second map - take the second map and produce more XML
    // If all goes well, the two generated blobs of XML should be the
    // same.  If anything goes wrong - we re-do it with lots of debug
    String complexXml = null;
    boolean success = false;
    debugFlag = false;
    try {
      complexXml = XMLMap.getXML(newMap, true);
      success = true;
    } catch (Exception e) {
      success = false;
    }

    // If we fail - do it again with deep levels of verbosity
    if (success) {
      unitTest(complexXml, false);
    } else {
      debugFlag = true;
      System.out.println("\n MISMATCH AND/OR SOME ERROR HAS OCCURED - REDO in VERBODE MODE");
      System.out.println("Starting out newMap=" + newMap);
      complexXml = XMLMap.getXML(newMap, true);
      unitTest(complexXml, false);
      debugFlag = false;
    }

    // A different example - iterating through nested sets - demonstrating the short form
    // of getSites() with the first parameter a string -the commented code below is the long form.

    // Map<String,Object> theMap = XMLMap.getFullMap(sitesText);
    // List<Map<String,Object>> theList = XMLMap.getList(theMap, "/sites/site");
    // for ( Map<String,Object> siteMap : theList) {

    // The short form using convenience method if you don't need the map for anything else
    System.out.println("\nParsing Sites Structure");
    for (Map<String, Object> siteMap : XMLMap.getList(sitesText, "/sites/site")) {
      System.out.println("Site=" + siteMap);
      System.out.println("Id=" + XMLMap.getString(siteMap, "/id"));
      for (Map<String, Object> toolMap : XMLMap.getList(siteMap, "/tools/tool")) {
        System.out.println("Tool=" + toolMap);
        System.out.println("ToolId=" + XMLMap.getString(toolMap, "/toolid"));
        for (Map<String, Object> property : XMLMap.getList(toolMap, "/properties/property")) {
          System.out.println("key=" + XMLMap.getString(property, "/key"));
          System.out.println("val=" + XMLMap.getString(property, "/val"));
        }
      }
    }

    // Lets parse some RSS as a final kind of easy but quite practical test
    debugFlag = false;
    System.out.println("\nParsing RSS Feed");
    // System.out.println(XMLMap.prettyPrint(rssText));
    Map<String, Object> rssFullMap = XMLMap.getFullMap(rssText);
    System.out.println("RSS Full Map\n" + rssFullMap);
    System.out.println("Rss Version=" + XMLMap.getString(rssFullMap, "/rss!version"));
    System.out.println("Chan-desc=" + XMLMap.getString(rssFullMap, "/rss/channel/description"));
    System.out.println("Chan-title=" + XMLMap.getString(rssFullMap, "/rss/channel/title"));

    Map<String, String> rssStringMap = XMLMap.flattenMap(rssFullMap);
    System.out.println("RSS Flat String Only Map\n" + rssStringMap);
    System.out.println("Rss Version=" + rssStringMap.get("/rss!version"));
    System.out.println("Chan-desc=" + rssStringMap.get("/rss/channel/description"));
    System.out.println("Chan-title=" + rssStringMap.get("/rss/channel/title"));

    for (Map<String, Object> rssItem : XMLMap.getList(rssFullMap, "/rss/channel/item")) {
      System.out.println("=== Item ===");
      System.out.println(" Item-title=" + XMLMap.getString(rssItem, "/title"));
      System.out.println(" Item-description=" + XMLMap.getString(rssItem, "/description"));
      System.out.println(" Item-link=" + XMLMap.getString(rssItem, "/link"));
    }
  }
  protected void processOutcomeXml(
      HttpServletRequest request,
      HttpServletResponse response,
      String lti_message_type,
      String sourcedid,
      IMSPOXRequest pox)
      throws java.io.IOException {
    // Things look good - time to process the grade
    boolean isRead = BasicLTIUtil.equals(lti_message_type, "readResultRequest");
    boolean isDelete = BasicLTIUtil.equals(lti_message_type, "deleteResultRequest");

    Map<String, String> bodyMap = pox.getBodyMap();
    String result_resultscore_textstring =
        bodyMap.get("/resultRecord/result/resultScore/textString");
    String result_resultdata_text = bodyMap.get("/resultRecord/result/resultData/text");
    String sourced_id = bodyMap.get("/resultRecord/result/sourcedId");
    // System.out.println("comment="+result_resultdata_text);
    // System.out.println("grade="+result_resultscore_textstring);

    if (BasicLTIUtil.isBlank(result_resultscore_textstring) && !isRead && !isDelete) {
      doErrorXML(request, response, pox, "outcomes.missing", "result_resultscore_textstring", null);
      return;
    }

    // Lets return an XML Response
    Map<String, Object> theMap = new TreeMap<String, Object>();
    String theGrade = null;
    boolean success = false;
    String message = null;
    Object retval = null;
    boolean strict = ServerConfigurationService.getBoolean(SakaiBLTIUtil.LTI_STRICT, false);

    try {
      Double dGrade;
      if (isRead) {
        retval = SakaiBLTIUtil.getGrade(sourcedid, request, ltiService);
        String sGrade = "";
        String comment = "";
        if (retval instanceof Map) {
          Map grade = (Map) retval;
          comment = (String) grade.get("comment");
          dGrade = (Double) grade.get("grade");
          if (dGrade != null) {
            sGrade = dGrade.toString();
          }
        } else {
          Object check = SakaiBLTIUtil.checkSourceDid(sourcedid, request, ltiService);
          if (check instanceof Boolean && ((Boolean) check)) {
            // Read fail with Good SourceDID is treated as empty
          } else {
            doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null);
            return;
          }
        }

        theMap.put("/readResultResponse/result/sourcedId", sourced_id);
        theMap.put("/readResultResponse/result/resultScore/textString", sGrade);
        theMap.put("/readResultResponse/result/resultScore/language", "en");
        if (!strict) {
          theMap.put("/readResultResponse/result/resultData/text", comment);
        }
        message = "Result read";
      } else if (isDelete) {
        retval = SakaiBLTIUtil.deleteGrade(sourcedid, request, ltiService);
        if (retval instanceof String) {
          doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null);
          return;
        }
        theMap.put("/deleteResultResponse", "");
        message = "Result deleted";
      } else {
        dGrade = new Double(result_resultscore_textstring);
        if (dGrade < 0.0 || dGrade > 1.0) {
          throw new Exception("Grade out of range");
        }
        dGrade = new Double(result_resultscore_textstring);
        retval =
            SakaiBLTIUtil.setGrade(sourcedid, request, ltiService, dGrade, result_resultdata_text);
        if (retval instanceof String) {
          doErrorXML(request, response, pox, "outcomes.fail", (String) retval, null);
          return;
        }
        theMap.put("/replaceResultResponse", "");
        message = "Result replaced";
      }

      success = true;
    } catch (Exception e) {
      doErrorXML(request, response, pox, "outcome.grade.fail", e.getMessage(), e);
    }

    if (!success) return;

    String output = null;
    String theXml = "";
    if (theMap.size() > 0) theXml = XMLMap.getXMLFragment(theMap, true);
    output = pox.getResponseSuccess(message, theXml);

    response.setContentType("application/xml");
    PrintWriter out = response.getWriter();
    out.println(output);
    M_log.debug(output);
  }
  @SuppressWarnings("unchecked")
  protected void doPostXml(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String ipAddress = request.getRemoteAddr();

    M_log.debug("LTI POX Service request from IP=" + ipAddress);

    String allowOutcomes =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED,
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT);
    if (!"true".equals(allowOutcomes)) allowOutcomes = null;

    if (allowOutcomes == null) {
      M_log.warn("LTI Services are disabled IP=" + ipAddress);
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      return;
    }

    IMSPOXRequest pox = new IMSPOXRequest(request);
    if (!pox.valid) {
      doErrorXML(request, response, pox, "pox.invalid", pox.errorMessage, null);
      return;
    }

    // check lti_message_type
    String lti_message_type = pox.getOperation();

    String sourcedid = null;
    String message_type = null;
    if (M_log.isDebugEnabled()) M_log.debug("POST\n" + XMLMap.prettyPrint(pox.postBody));
    Map<String, String> bodyMap = pox.getBodyMap();
    if (("replaceResultRequest".equals(lti_message_type)
            || "readResultRequest".equals(lti_message_type)
            || "deleteResultRequest".equals(lti_message_type))
        && allowOutcomes != null) {
      sourcedid = bodyMap.get("/resultRecord/sourcedGUID/sourcedId");
      message_type = "basicoutcome";
    } else {
      String output = pox.getResponseUnsupported("Not supported " + lti_message_type);
      response.setContentType("application/xml");
      PrintWriter out = response.getWriter();
      out.println(output);
      return;
    }

    // No point continuing without a sourcedid
    if (BasicLTIUtil.isBlank(sourcedid)) {
      doErrorXML(request, response, pox, "outcomes.missing", "sourcedid", null);
      return;
    }

    // Handle the outcomes here using the new SakaiBLTIUtil code
    if (allowOutcomes != null && "basicoutcome".equals(message_type)) {
      processOutcomeXml(request, response, lti_message_type, sourcedid, pox);
      return;
    }

    // Truncate this to the maximum length to insure no cruft at the end
    if (sourcedid.length() > 2048) sourcedid = sourcedid.substring(0, 2048);

    // Attempt to parse the sourcedid, any failure is fatal
    String placement_id = null;
    String signature = null;
    String user_id = null;
    try {
      int pos = sourcedid.indexOf(":::");
      if (pos > 0) {
        signature = sourcedid.substring(0, pos);
        String dec2 = sourcedid.substring(pos + 3);
        pos = dec2.indexOf(":::");
        user_id = dec2.substring(0, pos);
        placement_id = dec2.substring(pos + 3);
      }
    } catch (Exception e) {
      // Log some detail for ourselves
      M_log.warn(
          "Unable to decrypt result_sourcedid IP=" + ipAddress + " Error=" + e.getMessage(), e);
      signature = null;
      placement_id = null;
      user_id = null;
    }

    // Send a more generic message back to the caller
    if (placement_id == null || user_id == null) {
      doErrorXML(
          request, response, pox, "outcomes.sourcedid", "missing user_id or placement_id", null);
      return;
    }

    M_log.debug("signature=" + signature);
    M_log.debug("user_id=" + user_id);
    M_log.debug("placement_id=" + placement_id);

    Properties pitch = SakaiBLTIUtil.getPropertiesFromPlacement(placement_id, ltiService);
    if (pitch == null) {
      M_log.debug("Error retrieving result_sourcedid information");
      doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    String siteId = pitch.getProperty(LTIService.LTI_SITE_ID);
    Site site = null;
    try {
      site = SiteService.getSite(siteId);
    } catch (Exception e) {
      M_log.debug("Error retrieving result_sourcedid site: " + e.getLocalizedMessage(), e);
    }

    // Send a more generic message back to the caller
    if (site == null) {
      doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    // Check the message signature using OAuth
    String oauth_consumer_key = pox.getOAuthConsumerKey();
    String oauth_secret = pitch.getProperty(LTIService.LTI_SECRET);
    M_log.debug("oauth_secret: " + oauth_secret);
    oauth_secret = SakaiBLTIUtil.decryptSecret(oauth_secret);
    M_log.debug("oauth_secret (decrypted): " + oauth_secret);

    String URL = SakaiBLTIUtil.getOurServletPath(request);
    pox.validateRequest(oauth_consumer_key, oauth_secret, request, URL);
    if (!pox.valid) {
      if (pox.base_string != null) {
        M_log.warn(pox.base_string);
      }
      doErrorXML(request, response, pox, "outcome.no.validate", oauth_consumer_key, null);
      return;
    }

    // Check the signature of the sourcedid to make sure it was not altered
    String placement_secret = pitch.getProperty(LTIService.LTI_PLACEMENTSECRET);

    // Send a generic message back to the caller
    if (placement_secret == null) {
      M_log.debug("placement_secret is null");
      doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    String pre_hash = placement_secret + ":::" + user_id + ":::" + placement_id;
    String received_signature = LegacyShaUtil.sha256Hash(pre_hash);
    M_log.debug("Received signature=" + signature + " received=" + received_signature);
    boolean matched = signature.equals(received_signature);

    String old_placement_secret = pitch.getProperty(LTIService.LTI_OLDPLACEMENTSECRET);
    if (old_placement_secret != null && !matched) {
      pre_hash = placement_secret + ":::" + user_id + ":::" + placement_id;
      received_signature = LegacyShaUtil.sha256Hash(pre_hash);
      M_log.debug("Received signature II=" + signature + " received=" + received_signature);
      matched = signature.equals(received_signature);
    }

    // Send a message back to the caller
    if (!matched) {
      doErrorXML(request, response, pox, "outcomes.sourcedid", "sourcedid", null);
      return;
    }

    response.setContentType("application/xml");
    PrintWriter writer = response.getWriter();
    String desc = "Message received and validated operation=" + pox.getOperation();
    String output = pox.getResponseUnsupported(desc);
    writer.println(output);
  }
  protected void processRoster(
      HttpServletRequest request,
      HttpServletResponse response,
      String lti_message_type,
      Site site,
      String siteId,
      String placement_id,
      Properties pitch,
      String user_id,
      Map<String, Object> theMap)
      throws java.io.IOException {
    // Check for permission in placement
    String allowRoster = pitch.getProperty(LTIService.LTI_ALLOWROSTER);
    if (!"on".equals(allowRoster)) {
      doError(
          request,
          response,
          theMap,
          "outcomes.invalid",
          "lti_message_type=" + lti_message_type,
          null);
      return;
    }

    String roleMapProp = pitch.getProperty("rolemap");
    String releaseName = pitch.getProperty(LTIService.LTI_SENDNAME);
    String releaseEmail = pitch.getProperty(LTIService.LTI_SENDEMAILADDR);
    String assignment = pitch.getProperty("assignment");
    String allowOutcomes =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED,
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT);
    if (!"true".equals(allowOutcomes)) allowOutcomes = null;

    String maintainRole = site.getMaintainRole();

    SakaiBLTIUtil.pushAdvisor();
    boolean success = false;
    try {
      List<Map<String, Object>> lm = new ArrayList<Map<String, Object>>();
      Set<Member> members = site.getMembers();
      Map<String, String> roleMap = SakaiBLTIUtil.convertRoleMapPropToMap(roleMapProp);
      for (Member member : members) {
        Map<String, Object> mm = new TreeMap<String, Object>();
        Role role = member.getRole();
        String ims_user_id = member.getUserId();
        mm.put("/user_id", ims_user_id);
        String ims_role = "Learner";

        // If there is a role mapping, it has precedence over site.update
        if (roleMap.containsKey(role.getId())) {
          ims_role = roleMap.get(role.getId());
        } else if (ComponentManager.get(AuthzGroupService.class)
            .isAllowed(ims_user_id, SiteService.SECURE_UPDATE_SITE, "/site/" + siteId)) {
          ims_role = "Instructor";
        }

        // Using "/role" is inconsistent with to
        // http://developers.imsglobal.org/ext_membership.html. It
        // should be roles. If we can determine that nobody is using
        // the role tag, we should remove it.

        mm.put("/role", ims_role);
        mm.put("/roles", ims_role);
        User user = null;
        if ("true".equals(allowOutcomes) && assignment != null) {
          user = UserDirectoryService.getUser(ims_user_id);
          String placement_secret = pitch.getProperty(LTIService.LTI_PLACEMENTSECRET);
          String result_sourcedid =
              SakaiBLTIUtil.getSourceDID(user, placement_id, placement_secret);
          if (result_sourcedid != null) mm.put("/lis_result_sourcedid", result_sourcedid);
        }

        if ("on".equals(releaseName) || "on".equals(releaseEmail)) {
          if (user == null) user = UserDirectoryService.getUser(ims_user_id);
          if ("on".equals(releaseName)) {
            mm.put("/person_name_given", user.getFirstName());
            mm.put("/person_name_family", user.getLastName());
            mm.put("/person_name_full", user.getDisplayName());
          }
          if ("on".equals(releaseEmail)) {
            mm.put("/person_contact_email_primary", user.getEmail());
            mm.put("/person_sourcedid", user.getEid());
          }
        }

        Collection groups = site.getGroupsWithMember(ims_user_id);

        if (groups.size() > 0) {
          List<Map<String, Object>> lgm = new ArrayList<Map<String, Object>>();
          for (Iterator i = groups.iterator(); i.hasNext(); ) {
            Group group = (Group) i.next();
            Map<String, Object> groupMap = new HashMap<String, Object>();
            groupMap.put("/id", group.getId());
            groupMap.put("/title", group.getTitle());
            groupMap.put("/set", new HashMap(groupMap));
            lgm.add(groupMap);
          }
          mm.put("/groups/group", lgm);
        }

        lm.add(mm);
      }
      theMap.put("/message_response/members/member", lm);
      success = true;
    } catch (Exception e) {
      doError(request, response, theMap, "memberships.fail", "", e);
    } finally {
      SakaiBLTIUtil.popAdvisor();
    }

    if (!success) return;

    theMap.put("/message_response/statusinfo/codemajor", "Success");
    theMap.put("/message_response/statusinfo/severity", "Status");
    theMap.put("/message_response/statusinfo/codeminor", "fullsuccess");
    String theXml = XMLMap.getXML(theMap, true);
    PrintWriter out = response.getWriter();
    out.println(theXml);
    M_log.debug(theXml);
  }
  protected void processOutcome(
      HttpServletRequest request,
      HttpServletResponse response,
      String lti_message_type,
      String sourcedid,
      Map<String, Object> theMap)
      throws java.io.IOException {
    // Things look good - time to process the grade
    boolean isRead = BasicLTIUtil.equals(lti_message_type, "basic-lis-readresult");
    boolean isDelete = BasicLTIUtil.equals(lti_message_type, "basic-lis-deleteresult");

    String result_resultscore_textstring = request.getParameter("result_resultscore_textstring");
    String result_resultdata_text = request.getParameter("result_resultdata_text");

    if (BasicLTIUtil.isBlank(result_resultscore_textstring) && !isRead) {
      doError(request, response, theMap, "outcomes.missing", "result_resultscore_textstring", null);
      return;
    }

    String theGrade = null;
    boolean success = false;
    Object retval = null;

    try {
      Double dGrade;
      if (isRead) {
        retval = SakaiBLTIUtil.getGrade(sourcedid, request, ltiService);
        if (retval instanceof Map) {
          Map grade = (Map) retval;
          dGrade = (Double) grade.get("grade");
          theMap.put("/message_response/result/resultscore/textstring", dGrade.toString());
          theMap.put("/message_response/result/resultdata/text", (String) grade.get("comment"));
        } else {
          // Read fail with Good SourceDID is treated as empty
          Object check = SakaiBLTIUtil.checkSourceDid(sourcedid, request, ltiService);
          if (check instanceof Boolean && ((Boolean) check)) {
            theMap.put("/message_response/result/resultscore/textstring", "");
            theMap.put("/message_response/result/resultdata/text", "");
          } else {
            doError(request, response, theMap, "outcome.fail", (String) retval, null);
            return;
          }
        }
      } else if (isDelete) {
        retval = SakaiBLTIUtil.deleteGrade(sourcedid, request, ltiService);
      } else {
        dGrade = new Double(result_resultscore_textstring);
        retval =
            SakaiBLTIUtil.setGrade(sourcedid, request, ltiService, dGrade, result_resultdata_text);
      }
      success = true;
      theMap.put("/message_response/statusinfo/codemajor", "Success");
      theMap.put("/message_response/statusinfo/severity", "Status");
      theMap.put("/message_response/statusinfo/codeminor", "fullsuccess");
    } catch (Exception e) {
      doError(request, response, theMap, "outcome.grade.fail", "", e);
    }

    if (!success) return;

    String theXml = XMLMap.getXML(theMap, true);
    PrintWriter out = response.getWriter();
    out.println(theXml);
  }
  protected void processSetting(
      HttpServletRequest request,
      HttpServletResponse response,
      String lti_message_type,
      Site site,
      String siteId,
      String placement_id,
      Properties pitch,
      String user_id,
      Map<String, Object> theMap)
      throws java.io.IOException {
    String setting = null;

    // Check for permission in placement
    String allowSetting = pitch.getProperty(LTIService.LTI_ALLOWSETTINGS);
    if (!"on".equals(allowSetting)) {
      doError(
          request,
          response,
          theMap,
          "outcomes.invalid",
          "lti_message_type=" + lti_message_type,
          null);
      return;
    }

    SakaiBLTIUtil.pushAdvisor();
    boolean success = false;
    try {
      if ("basic-lti-loadsetting".equals(lti_message_type)) {
        setting = pitch.getProperty(LTIService.LTI_SETTINGS_EXT);
        // Remove this after the DB conversion for SAK-25621 is completed
        // It is harmless until LTI 2.0 starts to get heavy use.
        if (setting == null) {
          setting = pitch.getProperty(LTIService.LTI_SETTINGS);
        }
        if (setting != null) {
          theMap.put("/message_response/setting/value", setting);
        }
        success = true;
      } else {
        if (SakaiBLTIUtil.isPlacement(placement_id)) {
          ToolConfiguration placement = SiteService.findTool(placement_id);
          if ("basic-lti-savesetting".equals(lti_message_type)) {
            setting = request.getParameter("setting");
            if (setting == null) {
              M_log.warn("No setting parameter");
              doError(request, response, theMap, "setting.empty", "", null);
            } else {
              if (setting.length() > 8096) setting = setting.substring(0, 8096);
              placement.getPlacementConfig().setProperty("toolsetting", setting);
            }
          } else if ("basic-lti-deletesetting".equals(lti_message_type)) {
            placement.getPlacementConfig().remove("toolsetting");
          }
          try {
            placement.save();
            success = true;
          } catch (Exception e) {
            doError(request, response, theMap, "setting.save.fail", "", e);
          }
        } else {
          Map<String, Object> content = null;
          String contentStr = pitch.getProperty("contentKey");
          Long contentKey = foorm.getLongKey(contentStr);
          if (contentKey >= 0) content = ltiService.getContentDao(contentKey, siteId);
          if (content != null) {
            if ("basic-lti-savesetting".equals(lti_message_type)) {
              setting = request.getParameter("setting");
              if (setting == null) {
                M_log.warn("No setting parameter");
                doError(request, response, theMap, "setting.empty", "", null);
              } else {
                if (setting.length() > 8096) setting = setting.substring(0, 8096);
                content.put(LTIService.LTI_SETTINGS_EXT, setting);
                success = true;
              }
            } else if ("basic-lti-deletesetting".equals(lti_message_type)) {
              content.put(LTIService.LTI_SETTINGS_EXT, null);
              success = true;
            }
            if (success) {
              Object result = ltiService.updateContentDao(contentKey, content, siteId);
              if (result instanceof String) {
                M_log.warn("Setting update failed: " + result);
                doError(request, response, theMap, "setting.fail", "", null);
                success = false;
              }
            }
          }
        }
      }
    } catch (Exception e) {
      doError(request, response, theMap, "setting.fail", "", e);
    } finally {
      SakaiBLTIUtil.popAdvisor();
    }

    if (!success) return;

    theMap.put("/message_response/statusinfo/codemajor", "Success");
    theMap.put("/message_response/statusinfo/severity", "Status");
    theMap.put("/message_response/statusinfo/codeminor", "fullsuccess");
    String theXml = XMLMap.getXML(theMap, true);
    PrintWriter out = response.getWriter();
    out.println(theXml);
  }