private final void updateSiteDetailsIfChanged( Site site, String context_title, String context_label) { boolean changed = false; if (BasicLTIUtil.isNotBlank(context_title) && !context_title.equals(site.getTitle())) { site.setTitle(context_title); changed = true; } if (BasicLTIUtil.isNotBlank(context_label) && !context_label.equals(site.getShortDescription())) { site.setShortDescription(context_label); changed = true; } if (changed) { try { SiteService.save(site); M_log.info( "Updated site=" + site.getId() + " title=" + context_title + " label=" + context_label); } catch (Exception e) { M_log.warn("Failed to update site title and/or label"); } } }
protected Site findOrCreateSite(Map payload, boolean trustedConsumer) throws LTIException { String context_id = (String) payload.get(BasicLTIConstants.CONTEXT_ID); String oauth_consumer_key = (String) payload.get("oauth_consumer_key"); String siteId = null; if (trustedConsumer) { siteId = context_id; } else { siteId = LegacyShaUtil.sha1Hash(oauth_consumer_key + ":" + context_id); } if (M_log.isDebugEnabled()) { M_log.debug("siteId=" + siteId); } final String context_title = (String) payload.get(BasicLTIConstants.CONTEXT_TITLE); final String context_label = (String) payload.get(BasicLTIConstants.CONTEXT_LABEL); Site site = null; // Get the site if it exists if (ServerConfigurationService.getBoolean( "basiclti.provider.lookupSitesByLTIContextIdProperty", false)) { try { site = findSiteByLTIContextId(context_id); if (site != null) { updateSiteDetailsIfChanged(site, context_title, context_label); return site; } } catch (Exception e) { if (M_log.isDebugEnabled()) { M_log.debug(e.getLocalizedMessage(), e); } } } else { try { site = SiteService.getSite(siteId); updateSiteDetailsIfChanged(site, context_title, context_label); return site; } catch (Exception e) { if (M_log.isDebugEnabled()) { M_log.debug(e.getLocalizedMessage(), e); } } } // If trusted and site does not exist, error, otherwise, create the site if (trustedConsumer) { throw new LTIException("launch.site.invalid", "siteId=" + siteId, null); } else { pushAdvisor(); try { String sakai_type = "project"; // BLTI-154. If an autocreation site template has been specced in sakai.properties, use it. String autoSiteTemplateId = ServerConfigurationService.getString("basiclti.provider.autositetemplate", null); boolean templateSiteExists = SiteService.siteExists(autoSiteTemplateId); if (!templateSiteExists) { M_log.warn( "A template site id was specced (" + autoSiteTemplateId + ") but no site with this id exists. A default lti site will be created instead."); } if (autoSiteTemplateId == null || !templateSiteExists) { // BLTI-151 If the new site type has been specified in sakai.properties, use it. sakai_type = ServerConfigurationService.getString("basiclti.provider.newsitetype", null); if (BasicLTIUtil.isBlank(sakai_type)) { // It wasn't specced in the props. Test for the ims course context type. final String context_type = (String) payload.get(BasicLTIConstants.CONTEXT_TYPE); if (BasicLTIUtil.equalsIgnoreCase(context_type, "course")) { sakai_type = "course"; } else { sakai_type = BasicLTIConstants.NEW_SITE_TYPE; } } site = SiteService.addSite(siteId, sakai_type); site.setType(sakai_type); } else { Site autoSiteTemplate = SiteService.getSite(autoSiteTemplateId); site = SiteService.addSite(siteId, autoSiteTemplate); } if (BasicLTIUtil.isNotBlank(context_title)) { site.setTitle(context_title); } if (BasicLTIUtil.isNotBlank(context_label)) { site.setShortDescription(context_label); } site.setJoinable(false); site.setPublished(true); site.setPubView(false); // record the original context_id to a site property site.getPropertiesEdit().addProperty(LTI_CONTEXT_ID, context_id); try { SiteService.save(site); M_log.info( "Created site=" + siteId + " label=" + context_label + " type=" + sakai_type + " title=" + context_title); } catch (Exception e) { throw new LTIException("launch.site.save", "siteId=" + siteId, e); } } catch (Exception e) { throw new LTIException("launch.create.site", "siteId=" + siteId, e); } finally { popAdvisor(); } } try { return SiteService.getSite(site.getId()); } catch (IdUnusedException e) { throw new LTIException("launch.site.invalid", "siteId=" + siteId, e); } }
private String addOrCreateTool(Map payload, boolean trustedConsumer, User user, Site site) throws LTIException { // Check if the site already has the tool String toolPlacementId = null; String tool_id = (String) payload.get("tool_id"); try { site = SiteService.getSite(site.getId()); ToolConfiguration toolConfig = site.getToolForCommonId(tool_id); if (toolConfig != null) { toolPlacementId = toolConfig.getId(); } } catch (Exception e) { M_log.warn(e.getLocalizedMessage(), e); throw new LTIException("launch.tool.search", "tool_id=" + tool_id, e); } if (M_log.isDebugEnabled()) { M_log.debug("toolPlacementId=" + toolPlacementId); } // If tool not in site, and we are a trusted consumer, error // Otherwise, add tool to the site ToolConfiguration toolConfig = null; if (BasicLTIUtil.isBlank(toolPlacementId)) { try { SitePage sitePageEdit = null; sitePageEdit = site.addPage(); sitePageEdit.setTitle(tool_id); toolConfig = sitePageEdit.addTool(); toolConfig.setTool(tool_id, ToolManager.getTool(tool_id)); toolConfig.setTitle(tool_id); Properties propsedit = toolConfig.getPlacementConfig(); propsedit.setProperty( BASICLTI_RESOURCE_LINK, (String) payload.get(BasicLTIConstants.RESOURCE_LINK_ID)); pushAdvisor(); try { SiteService.save(site); M_log.info("Tool added, tool_id=" + tool_id + ", siteId=" + site.getId()); } catch (Exception e) { throw new LTIException( "launch.site.save", "tool_id=" + tool_id + ", siteId=" + site.getId(), e); } finally { popAdvisor(); } toolPlacementId = toolConfig.getId(); } catch (Exception e) { throw new LTIException( "launch.tool.add", "tool_id=" + tool_id + ", siteId=" + site.getId(), e); } } // Get ToolConfiguration for tool if not already setup if (toolConfig == null) { toolConfig = site.getToolForCommonId(tool_id); } // Check user has access to this tool in this site if (!ToolManager.isVisible(site, toolConfig)) { M_log.warn( "Not allowed to access tool user_id=" + user.getId() + " site=" + site.getId() + " tool=" + tool_id); throw new LTIException( "launch.site.tool.denied", "user_id=" + user.getId() + " site=" + site.getId() + " tool=" + tool_id, null); } return toolPlacementId; }
protected void validate(Map payload, boolean isTrustedConsumer) throws LTIException { // check parameters String lti_message_type = (String) payload.get(BasicLTIConstants.LTI_MESSAGE_TYPE); String lti_version = (String) payload.get(BasicLTIConstants.LTI_VERSION); String oauth_consumer_key = (String) payload.get("oauth_consumer_key"); String resource_link_id = (String) payload.get(BasicLTIConstants.RESOURCE_LINK_ID); String user_id = (String) payload.get(BasicLTIConstants.USER_ID); String context_id = (String) payload.get(BasicLTIConstants.CONTEXT_ID); boolean launch = true; if (BasicLTIUtil.equals(lti_message_type, "basic-lti-launch-request")) { launch = true; } else if (BasicLTIUtil.equals(lti_message_type, "ContentItemSelectionRequest")) { launch = false; } else { throw new LTIException("launch.invalid", "lti_message_type=" + lti_message_type, null); } if (!BasicLTIUtil.equals(lti_version, "LTI-1p0")) { throw new LTIException("launch.invalid", "lti_version=" + lti_version, null); } if (BasicLTIUtil.isBlank(oauth_consumer_key)) { throw new LTIException("launch.missing", "oauth_consumer_key", null); } if (launch && BasicLTIUtil.isBlank(resource_link_id)) { throw new LTIException("launch.missing", "resource_link_id", null); } if (BasicLTIUtil.isBlank(user_id)) { throw new LTIException("launch.missing", "user_id", null); } if (M_log.isDebugEnabled()) { M_log.debug("user_id=" + user_id); } // check tool_id String tool_id = (String) payload.get("tool_id"); if (tool_id == null) { throw new LTIException("launch.tool_id.required", null, null); } // Trim off the leading slash and any trailing space tool_id = tool_id.substring(1).trim(); if (M_log.isDebugEnabled()) { M_log.debug("tool_id=" + tool_id); } // store modified tool_id back in payload payload.put("tool_id", tool_id); final String allowedToolsConfig = ServerConfigurationService.getString("basiclti.provider.allowedtools", ""); final String[] allowedTools = allowedToolsConfig.split(":"); final List<String> allowedToolsList = Arrays.asList(allowedTools); if (launch && allowedTools != null && !allowedToolsList.contains(tool_id)) { throw new LTIException("launch.tool.notallowed", tool_id, null); } final Tool toolCheck = ToolManager.getTool(tool_id); if (launch && toolCheck == null) { throw new LTIException("launch.tool.notfound", tool_id, null); } // Check for the ext_sakai_provider_eid param. If set, this will contain the eid that we are to // use // in place of using the user_id parameter // WE still need that parameter though, so translate it from the given eid. boolean useProvidedEid = false; String ext_sakai_provider_eid = (String) payload.get(BasicLTIConstants.EXT_SAKAI_PROVIDER_EID); if (BasicLTIUtil.isNotBlank(ext_sakai_provider_eid)) { useProvidedEid = true; try { user_id = UserDirectoryService.getUserId(ext_sakai_provider_eid); } catch (Exception e) { M_log.error(e.getLocalizedMessage(), e); throw new LTIException( "launch.provided.eid.invalid", "ext_sakai_provider_eid=" + ext_sakai_provider_eid, e); } } if (M_log.isDebugEnabled()) { M_log.debug("ext_sakai_provider_eid=" + ext_sakai_provider_eid); } // Contextualize the context_id with the OAuth consumer key // Also use the resource_link_id for the context_id if we did not get a context_id // BLTI-31: if trusted, context_id is required and use the param without modification if (BasicLTIUtil.isBlank(context_id)) { if (isTrustedConsumer) { throw new LTIException("launch.missing", context_id, null); } else { context_id = "res:" + resource_link_id; payload.put(BasicLTIConstants.CONTEXT_ID, context_id); } } // Check if context_id is simply a ~. If so, get the id of that user's My Workspace site // and use that to construct the full context_id if (BasicLTIUtil.equals(context_id, "~")) { if (useProvidedEid) { String userSiteId = null; try { userSiteId = SiteService.getUserSiteId(user_id); } catch (Exception e) { M_log.warn("Failed to get My Workspace site for user_id:" + user_id); M_log.error(e.getLocalizedMessage(), e); throw new LTIException("launch.user.site.unknown", "user_id=" + user_id, e); } context_id = userSiteId; payload.put(BasicLTIConstants.CONTEXT_ID, context_id); } } if (M_log.isDebugEnabled()) { M_log.debug("context_id=" + context_id); } // Lookup the secret final String configPrefix = "basiclti.provider." + oauth_consumer_key + "."; final String oauth_secret = ServerConfigurationService.getString(configPrefix + "secret", null); if (oauth_secret == null) { throw new LTIException("launch.key.notfound", oauth_consumer_key, null); } final OAuthMessage oam = (OAuthMessage) payload.get("oauth_message"); final String forcedURIScheme = ServerConfigurationService.getString("basiclti.provider.forcedurischeme", null); if (forcedURIScheme != null) { try { URI testURI = new URI(oam.URL); URI newURI = new URI(forcedURIScheme, testURI.getSchemeSpecificPart(), null); oam.URL = newURI.toString(); } catch (URISyntaxException use) { } } final OAuthValidator oav = new SimpleOAuthValidator(); final OAuthConsumer cons = new OAuthConsumer( "about:blank#OAuth+CallBack+NotUsed", oauth_consumer_key, oauth_secret, null); final OAuthAccessor acc = new OAuthAccessor(cons); String base_string = null; try { base_string = OAuthSignatureMethod.getBaseString(oam); } catch (Exception e) { M_log.error(e.getLocalizedMessage(), e); base_string = null; } try { oav.validateMessage(oam, acc); } catch (Exception e) { M_log.warn("Provider failed to validate message"); M_log.warn(e.getLocalizedMessage(), e); if (base_string != null) { M_log.warn(base_string); } throw new LTIException("launch.no.validate", context_id, e); } final Session sess = SessionManager.getCurrentSession(); if (sess == null) { throw new LTIException("launch.no.session", context_id, null); } }
@SuppressWarnings("unchecked") protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String ipAddress = request.getRemoteAddr(); if (M_log.isDebugEnabled()) { M_log.debug("Basic LTI Provider request from IP=" + ipAddress); } String enabled = ServerConfigurationService.getString("basiclti.provider.enabled", null); if (enabled == null || !("true".equals(enabled))) { M_log.warn("Basic LTI Provider is Disabled IP=" + ipAddress); response.sendError(HttpServletResponse.SC_FORBIDDEN, "Basic LTI Provider is Disabled"); return; } if ("/casa.json".equals(request.getPathInfo())) { if (ServerConfigurationService.getBoolean("casa.provider.enabled", true)) { handleCASAList(request, response); return; } else { M_log.warn("CASA Provider is Disabled IP=" + ipAddress); response.sendError(HttpServletResponse.SC_FORBIDDEN, "CASA Provider is Disabled"); return; } } if ("/canvas-config.xml".equals(request.getPathInfo())) { if (ServerConfigurationService.getBoolean("canvas.config.enabled", true)) { handleCanvasConfig(request, response); return; } else { M_log.warn("Canvas config is Disabled IP=" + ipAddress); response.sendError(HttpServletResponse.SC_FORBIDDEN, "Canvas config is Disabled"); return; } } // If this is a LTI request of any kind, make sure we don't have any // prior payload in the session. if (BasicLTIUtil.isRequest(request)) { Session sess = SessionManager.getCurrentSession(); sess.removeAttribute("payload"); } // Check if we support ContentItem. // If we are doing ContentItem and have a payload and are not a launch // short-circuit to ContentItem if ("/content.item".equals(request.getPathInfo())) { if (!ServerConfigurationService.getBoolean("contentitem.provider", true)) { M_log.warn("ContentItem is Disabled IP=" + ipAddress); response.sendError(HttpServletResponse.SC_FORBIDDEN, "ContentItem is Disabled"); return; } else { Session sess = SessionManager.getCurrentSession(); Map session_payload = (Map) sess.getAttribute("payload"); if (session_payload != null) { // Post-Login requests to content.item M_log.debug("ContentItem already logged in " + sess.getUserId()); handleContentItem(request, response, session_payload); return; } } } if (M_log.isDebugEnabled()) { Map<String, String[]> params = (Map<String, String[]>) request.getParameterMap(); for (Map.Entry<String, String[]> param : params.entrySet()) { M_log.debug(param.getKey() + ":" + param.getValue()[0]); } } Map payload = getPayloadAsMap(request); // Get the list of highly trusted consumers from sakai.properties. // If the incoming consumer is highly trusted, we use the context_id and // site_id as is, // ie without prefixing them with the oauth_consumer_key first. // We also don't both checking their roles in the site. boolean isTrustedConsumer = BasicLTIProviderUtil.isHighlyTrustedConsumer(payload); /* * Get the list of email trusted consumers from sakai.properties. If the * incoming consumer is email trusted, we use the email address provided * by the consumer and look up the "user" info from sakai instead of * consumer's. This use case is especially valuable if 2 different LMS's * acting as TP and TC referring to same user and can be uniquely * identified by email address. more details SAK-29372 */ boolean isEmailTrustedConsumer = BasicLTIProviderUtil.isEmailTrustedConsumer(payload); /* * Checking if the email trusted consumer property and trusted consumer * and not both enabled. the case would be an error condition */ if (isTrustedConsumer && isEmailTrustedConsumer) { M_log.warn( "Both Email Trusted and Trusted Consumer property is enabled, this is invalid IP=" + ipAddress); response.sendError( HttpServletResponse.SC_FORBIDDEN, "Both Email Trusted and Trusted Consumer property is enabled, this is invalid "); return; } try { invokeProcessors(payload, isTrustedConsumer, ProcessingState.beforeValidation); validate(payload, isTrustedConsumer); invokeProcessors(payload, isTrustedConsumer, ProcessingState.afterValidation); User user = userFinderOrCreator.findOrCreateUser(payload, isTrustedConsumer, isEmailTrustedConsumer); invokeProcessors(payload, isTrustedConsumer, ProcessingState.afterUserCreation, user); // Check if we are loop-backing on the same server, and already logged in as same user Session sess = SessionManager.getCurrentSession(); String serverUrl = SakaiBLTIUtil.getOurServerUrl(); String ext_sakai_server = (String) payload.get("ext_sakai_server"); if ("/content.item".equals(request.getPathInfo()) && isTrustedConsumer && ext_sakai_server != null && ext_sakai_server.equals(serverUrl) && user.getId().equals(sess.getUserId())) { M_log.debug("ContentItem looping back as " + sess.getUserId()); sess.setAttribute("payload", payload); handleContentItem(request, response, payload); return; } loginUser(ipAddress, user); // Re-grab the session sess = SessionManager.getCurrentSession(); invokeProcessors(payload, isTrustedConsumer, ProcessingState.afterLogin, user); // This needs to happen after login, when we have a session for the user. userLocaleSetter.setupUserLocale(payload, user, isTrustedConsumer, isEmailTrustedConsumer); userPictureSetter.setupUserPicture(payload, user, isTrustedConsumer, isEmailTrustedConsumer); // The first launch of content.item - no site needed if ("/content.item".equals(request.getPathInfo())) { M_log.debug("ContentItem inital external login " + sess.getUserId()); sess.setAttribute("payload", payload); handleContentItem(request, response, payload); return; } Site site = findOrCreateSite(payload, isTrustedConsumer); invokeProcessors(payload, isTrustedConsumer, ProcessingState.afterSiteCreation, user, site); siteEmailPreferenceSetter.setupUserEmailPreferenceForSite( payload, user, site, isTrustedConsumer); site = siteMembershipUpdater.addOrUpdateSiteMembership(payload, isTrustedConsumer, user, site); invokeProcessors(payload, isTrustedConsumer, ProcessingState.afterSiteMembership, user, site); String toolPlacementId = addOrCreateTool(payload, isTrustedConsumer, user, site); invokeProcessors(payload, isTrustedConsumer, ProcessingState.beforeLaunch, user, site); syncSiteMembershipsOnceThenSchedule(payload, site, isTrustedConsumer, isEmailTrustedConsumer); // Construct a URL to this tool StringBuilder url = new StringBuilder(); url.append(SakaiBLTIUtil.getOurServerUrl()); url.append(ServerConfigurationService.getString("portalPath", "/portal")); url.append("/tool-reset/"); url.append(toolPlacementId); url.append("?panel=Main"); if (M_log.isDebugEnabled()) { M_log.debug("url=" + url.toString()); } // String toolLink = ServerConfigurationService.getPortalUrl()+ "/tool-reset/" + placement_id // + "?panel=Main"; // Compensate for bug in getPortalUrl() // toolLink = toolLink.replace("IMS BLTI Portlet", "portal"); response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_FOUND); response.sendRedirect(url.toString()); } catch (LTIException ltiException) { doError( request, response, ltiException.getErrorKey(), ltiException.getMessage(), ltiException.getCause()); } /* PrintWriter out = response.getWriter(); out.println("<body><div style=\"text-align: center\">"); out.println(" <br/> <br/> <br/> <br/>"); out.println(" <br/> <br/> <br/> <br/>"); out.println("<a href=\"" + url.toString() + "\">"); out.println("<span id=\"hideme\">" + rb.getString("launch.continue") + "</span>"); out.println("</a>"); out.println(" <script language=\"javascript\"> \n" + " document.getElementById(\"hideme\").style.display = \"none\";\n" + " location.href=\"" + url.toString() + "\";\n" + " </script> \n"); out.println("</div>"); out.println("</body>"); out.close(); */ }
private void handleContentItem( HttpServletRequest request, HttpServletResponse response, Map payload) throws ServletException, IOException { String allowedToolsConfig = ServerConfigurationService.getString("basiclti.provider.allowedtools", ""); String[] allowedTools = allowedToolsConfig.split(":"); List<String> allowedToolsList = Arrays.asList(allowedTools); String tool_id = (String) request.getParameter("install"); if (tool_id == null) { ArrayList<Tool> tools = new ArrayList<Tool>(); for (String toolId : allowedToolsList) { Tool theTool = ToolManager.getTool(toolId); if (theTool == null) continue; tools.add(theTool); } request.setAttribute("tools", tools); } else { if (!allowedToolsList.contains(tool_id)) { doError(request, response, "launch.tool.notallowed", tool_id, null); return; } final Tool toolCheck = ToolManager.getTool(tool_id); if (toolCheck == null) { doError(request, response, "launch.tool.notfound", tool_id, null); return; } String content_item_return_url = (String) payload.get("content_item_return_url"); if (content_item_return_url == null) { doError(request, response, "content_item.return_url.notfound", tool_id, null); return; } ContentItemResponse resp = SakaiContentItemUtil.getContentItemResponse(tool_id); if (resp == null) { doError(request, response, "launch.tool.notfound", tool_id, null); return; } String content_items = resp.prettyPrintLog(); // Set up the return Map<String, String> ltiMap = new HashMap<String, String>(); Map<String, String> extra = new HashMap<String, String>(); ltiMap.put( BasicLTIConstants.LTI_MESSAGE_TYPE, BasicLTIConstants.LTI_MESSAGE_TYPE_CONTENTITEMSELECTION); ltiMap.put(BasicLTIConstants.LTI_VERSION, BasicLTIConstants.LTI_VERSION_1); ltiMap.put("content_items", content_items); String data = (String) payload.get("data"); if (data != null) ltiMap.put("data", data); M_log.debug("ltiMap=" + ltiMap); boolean dodebug = M_log.isDebugEnabled(); boolean autosubmit = false; String launchtext = rb.getString("content_item.install.button"); String back_to_store = rb.getString("content_item.back.to.store"); extra.put( "button_html", "<input type=\"submit\" value=\"" + back_to_store + "\"onclick=\"location.href='content.item'; return false;\">"); String launch_html = BasicLTIUtil.postLaunchHTML( ltiMap, content_item_return_url, launchtext, autosubmit, dodebug, extra); request.setAttribute("back_to_store", rb.getString("content_item.back.to.store")); request.setAttribute("install", tool_id); request.setAttribute("launch_html", launch_html); request.setAttribute("tool", toolCheck); } // Forward to the JSP ServletContext sc = this.getServletContext(); RequestDispatcher rd = sc.getRequestDispatcher("/contentitem.jsp"); try { rd.forward(request, response); } catch (Exception e) { e.printStackTrace(); } }
private void syncSiteMembershipsOnceThenSchedule( Map payload, Site site, boolean isTrustedConsumer, boolean isEmailTrustedConsumer) throws LTIException { if (isTrustedConsumer) return; M_log.debug("synchSiteMembershipsOnceThenSchedule"); if (!ServerConfigurationService.getBoolean(SakaiBLTIUtil.INCOMING_ROSTER_ENABLED, false)) { M_log.info("LTI Memberships synchronization disabled."); return; } final String membershipsUrl = (String) payload.get("ext_ims_lis_memberships_url"); if (!BasicLTIUtil.isNotBlank(membershipsUrl)) { M_log.info("LTI Memberships extension is not supported."); return; } if (M_log.isDebugEnabled()) M_log.debug("Memberships URL: " + membershipsUrl); final String membershipsId = (String) payload.get("ext_ims_lis_memberships_id"); if (!BasicLTIUtil.isNotBlank(membershipsId)) { M_log.info("No memberships id supplied. Memberships will NOT be synchronized."); return; } final String siteId = site.getId(); // If this site has already been scheduled, then we do nothing. if (ltiService.getMembershipsJob(siteId) != null) { if (M_log.isDebugEnabled()) { M_log.debug( "Site '" + siteId + "' already scheduled for memberships sync. Doing nothing ..."); } return; } final String oauth_consumer_key = (String) payload.get(OAuth.OAUTH_CONSUMER_KEY); // This is non standard. Moodle's core LTI plugin does not currently do memberships and // a fix for this has been proposed at https://tracker.moodle.org/browse/MDL-41724. I don't // think this will ever become core and the first time memberships will appear in core lti // is with LTI2. At that point this code will be replaced with standard LTI2 JSON type stuff. String lms = (String) payload.get("ext_lms"); final String callbackType = (BasicLTIUtil.isNotBlank(lms) && lms.equals("moodle-2")) ? "ext-moodle-2" : (String) payload.get(BasicLTIConstants.LTI_VERSION); (new Thread( new Runnable() { public void run() { long then = 0L; if (M_log.isDebugEnabled()) { M_log.debug("Starting memberships sync."); then = (new Date()).getTime(); } siteMembershipsSynchroniser.synchroniseSiteMemberships( siteId, membershipsId, membershipsUrl, oauth_consumer_key, isEmailTrustedConsumer, callbackType); if (M_log.isDebugEnabled()) { long now = (new Date()).getTime(); M_log.debug( "Memberships sync finished. It took " + ((now - then) / 1000) + " seconds."); } } }, "org.sakaiproject.blti.ProviderServlet.MembershipsSync")) .start(); ltiService.insertMembershipsJob( siteId, membershipsId, membershipsUrl, oauth_consumer_key, callbackType); }