Java tutorial
package com.ublish.service; import java.io.IOException; import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import net.oauth.OAuthException; import net.oauth.OAuthMessage; import net.oauth.signature.OAuthSignatureMethod; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import blackboard.data.content.Content; import blackboard.data.course.Course; import blackboard.data.course.CourseMembership; import blackboard.data.course.CourseMembership.Role; import blackboard.data.registry.CourseRegistryUtil; import blackboard.data.registry.SystemRegistryUtil; import blackboard.data.role.RoleUtil; import blackboard.data.user.User; import blackboard.persist.Id; import blackboard.platform.LicenseManagerFactory; import blackboard.platform.branding.BrandingUtil; import blackboard.platform.branding.PersonalStyleHelper; import blackboard.platform.context.Context; import blackboard.platform.gradebook2.impl.GradableItemDAO; import blackboard.platform.intl.BbLocale; import blackboard.platform.intl.LocaleManagerFactory; import blackboard.platform.plugin.PlugInUtil; import blackboard.platform.proxytool.ProxyToolConstants; import blackboard.platform.proxytool.ProxyToolUtil; import blackboard.util.Base64Codec; import blackboard.util.GeneralUtil; import blackboard.util.LocaleUtil; import blackboard.util.UrlUtil; import blackboard.util.UuidFactory; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.ublish.data.CustomParameters; import com.ublish.data.LaunchParameters; import com.ublish.data.LtiLaunch; import com.ublish.outcomes.consumer.LtiOutcomesSourcedIdResolver; @Service("basicLTIService") public class BasicLTIService { private static final Logger logger = LoggerFactory.getLogger(BasicLTIService.class); private static final String UTF8 = "UTF-8"; @Autowired private String b2Vendor; @Autowired private String b2Handle; @Autowired private String urlRegistryKey; @Autowired private String urlDefaultValue; @Autowired private String courseKeyRegistryKey; @Autowired private String courseSecretRegistryKey; public LtiLaunch buildToolLaunch(Context context, Course course, User user, CourseMembership membership) throws OAuthException { String consumerKey = CourseRegistryUtil.getString(course.getId(), courseKeyRegistryKey, ""); String consumerSecret = CourseRegistryUtil.getString(course.getId(), courseSecretRegistryKey, ""); if (StringUtils.isBlank(consumerKey) || StringUtils.isBlank(consumerSecret)) { throw new OAuthException( "Invalid Credentials: Please enter the Consumer Key and Secret for your myBusinessCourse course."); } LtiLaunch launch = new LtiLaunch(); launch.setLaunchParameters(addLaunchParameters(context, course, user, membership, null)); launch.setCustomParameters(addCustomParameters(context, course, null)); launch.setHttpMethod("POST"); launch.setUrl(SystemRegistryUtil.getString(urlRegistryKey, urlDefaultValue)); launch.setConsumerKey(consumerKey); launch.setConsumerSecret(consumerSecret); addOAuthParametersAndSign(launch); return launch; } public LtiLaunch buildContentLaunch(Context context, Course course, User user, CourseMembership membership, Content content) throws OAuthException { String consumerKey = CourseRegistryUtil.getString(course.getId(), courseKeyRegistryKey, ""); String consumerSecret = CourseRegistryUtil.getString(course.getId(), courseSecretRegistryKey, ""); if (StringUtils.isBlank(consumerKey) || StringUtils.isBlank(consumerSecret)) { if (!membership.getRole().equals(Role.INSTRUCTOR)) { throw new OAuthException( "Invalid Credentials: The credentials for thsi course are invalid. Please ask your instructor to enter the Consumer Key and Consumer Secret for your course on the myBusinessCourse Tools Page."); } else { throw new OAuthException( "Invalid Credentials: Please enter the Consumer Key and Secret for your course on the myBusinessCourse Tools page."); } } LtiLaunch launch = new LtiLaunch(); launch.setLaunchParameters(addLaunchParameters(context, course, user, membership, content)); launch.setCustomParameters(addCustomParameters(context, course, content)); launch.setHttpMethod("POST"); launch.setUrl(content.getUrl()); launch.setConsumerKey(consumerKey); launch.setConsumerSecret(consumerSecret); addOAuthParametersAndSign(launch); return launch; } private void addOAuthParametersAndSign(LtiLaunch launch) throws OAuthException { addOAuthParameters(launch); OAuthMessage message = new OAuthMessage(launch.getHttpMethod(), launch.getUrl(), Lists.newArrayList(launch.getParameters().entrySet())); String signature = getSignature(message, launch.getConsumerSecret()); logger.debug("OAuth Signature: {}", signature); launch.getLaunchParameters().setOauthSignature(signature); } private void addOAuthParameters(LtiLaunch launch) { LaunchParameters launchParameters = launch.getLaunchParameters(); launchParameters.setOauthCallback(LaunchParameters.OAUTH_CALLBACK_DEFAULT); launchParameters.setOauthConsumerKey(launch.getConsumerKey()); launchParameters.setOauthNonce(getNonce()); launchParameters.setOauthSignatureMethod(LaunchParameters.OAUTH_SIGNATURE_METHOD_HMACSHA1); launchParameters.setOauthTimestamp(getTimestamp()); launchParameters.setOauthVersion(LaunchParameters.OAUTH_VERSION_DEFAULT); } private LaunchParameters addLaunchParameters(Context context, Course course, User user, CourseMembership membership, Content content) { LaunchParameters launchParameters = new LaunchParameters(); // LTI Params launchParameters.setLtiVersion(LaunchParameters.BASIC_LTI_VERSION); launchParameters.setLtiMessageType(LaunchParameters.BASIC_LTI_MESSAGE_TYPE); //Resource if (content != null) { launchParameters.setResourceLinkId(content.getId().getExternalString()); if (!StringUtils.isBlank(content.getTitle())) { launchParameters.setResourceLinkTitle(content.getTitle()); } String description = content.getBody().getFormattedText(); if (!StringUtils.isBlank(description)) { launchParameters.setResourceLinkDescription(description); } } else { // add a random value as resourceLinkId. launchParameters.setResourceLinkId(UuidFactory.createUuid()); } //Tool Consumer launchParameters.setToolConsumerInstanceGuid(GeneralUtil.getSystemInstallationId()); String instanceName = GeneralUtil.getSystemInstanceName(); if (!StringUtils.isBlank(instanceName)) { launchParameters.setToolConsumerInstanceName(instanceName); } String sysadminEmail = GeneralUtil.getSystemAdminEmail(); if (!StringUtils.isEmpty(sysadminEmail)) { launchParameters.setToolConsumerInstanceContactEmail(sysadminEmail); } launchParameters.setToolConsumerInfoCode("Blackboard Learn"); launchParameters.setToolConsumerInfoVersion(GeneralUtil.getReleaseNumber()); launchParameters.setExtLms(String.format("bb-%s", LicenseManagerFactory.getInstance().getBuildNumber())); BbLocale locale = LocaleManagerFactory.getInstance().getLocale(); launchParameters.setLaunchPresentationLocale(locale.getLocale().replace('_', '-')); launchParameters.setLaunchPresentationReturnUrl(getBaseUrl(context)); launchParameters.setExtLaunchPresentationCssUrl(getCssUrls(context, course, user)); // User launchParameters.setUserId(user.getUuid()); launchParameters.setLisPersonNameGiven(user.getGivenName()); launchParameters.setLisPersonNameFamily(user.getFamilyName()); // use trim to fix a bug in the core product where there is always a leading space in the user name. launchParameters.setLisPersonNameFull(locale.formatName(user, BbLocale.Name.LONG).trim()); if (!StringUtils.isEmpty(user.getEmailAddress())) { launchParameters.setLisPersonContactEmailPrimary(user.getEmailAddress()); } String ltiRole = ProxyToolUtil.getInstance().getLtiRole(user, membership, ProxyToolConstants.LTI_VERSION_BASIC_LTI); launchParameters.setRoles(ltiRole); // Context launchParameters.setContextId(course.getUuid()); launchParameters.setContextLabel(course.getCourseId()); launchParameters.setContextTitle(course.getTitle()); // Content if (content != null && content.getLaunchInNewWindow()) { launchParameters.setLaunchPresentationDocumentTarget("window"); } else { launchParameters.setLaunchPresentationDocumentTarget("frame"); } addGradingParameters(membership, content, launchParameters); return launchParameters; } private void addGradingParameters(CourseMembership courseMembership, Content content, LaunchParameters launchParameters) { if (content == null) { // Skip this for tool launches. logger.debug("No content item found in the launch. Returning..."); return; } Id gradableItemId = GradableItemDAO.get().getIdFromContentId(content.getId()); if (Id.isValid(gradableItemId)) { // Only set this if the current user is a student - no other roles actually have grading results. String url = UrlUtil.calculateFullUrl(PlugInUtil.getUri(b2Vendor, b2Handle, "app/grading/lti11grade")); String sourcedId = null; if (RoleUtil.isStudentRole(courseMembership.getRole())) { sourcedId = LtiOutcomesSourcedIdResolver.toSourcedId(courseMembership.getId(), gradableItemId); launchParameters.setLisResultSourcedId(sourcedId); } // Change in conformance test: To support the use case of 'instructor is first user to access link // and tool wants to change instructor UI based on knowledge that grading is supported on the link' // we will now send the gradingurl for all roles, not just the student role (and passing null to // addGradingInformation for the sourcedId accomplishes that) launchParameters.setLisOutcomeServiceUrl(url); } } private CustomParameters addCustomParameters(Context context, Course course, Content content) { CustomParameters customParameters = new CustomParameters(); customParameters.setBlackboardCourseId(course.getUuid()); customParameters.setBlackboardUrl(getBaseUrl(context)); customParameters.setBlackboardB2Uri(PlugInUtil.getUri(b2Vendor, b2Handle, "")); if (content != null) { Map<String, String> customContentParameters = content.getExtendedData().getValues(); for (Entry<String, String> entry : customContentParameters.entrySet()) { customParameters.setParameter(entry.getKey(), entry.getValue()); } } return customParameters; } private String getBaseUrl(Context context) { return UrlUtil.calculateFullUrl(context.getRequest(), ""); } private String getCssUrls(Context context, Course course, User user) { List<String> cssUrls = BrandingUtil.getCssUrls(context.getRequest(), user, course, null, PersonalStyleHelper.isHighContrast(context.getRequest()), !LocaleUtil.isLeftToRight()); return Joiner.on(",").skipNulls().join(cssUrls); } private String getNonce() { return UuidFactory.createUuid(); } private String getTimestamp() { return Long.toString(new DateTime().getMillis() / 1000); } private String getBaseString(OAuthMessage message) throws URISyntaxException, IOException { return OAuthSignatureMethod.getBaseString(message); } private String getSignature(OAuthMessage message, String secret) throws OAuthException { try { String baseString = getBaseString(message); byte[] keyBytes = (secret + "&").getBytes(UTF8); SecretKey secretKey = new SecretKeySpec(keyBytes, LaunchParameters.OAUTH_SIGNATURE_METHOD_HMACSHA1); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(secretKey); return Base64Codec.encode(mac.doFinal(baseString.getBytes(UTF8))).trim(); } catch (Exception e) { throw new OAuthException("An exception occurred while creating the OAuth signature.", e); } } }