Access Keys:
Skip to content (Access Key - 0)

Overview

What is a Learning Environment Integration?

The Blackboard Learning Environment Integration feature allows another Learning Management System to co-exist and run within the Blackboard Learn. This integration is enabled by the Learning Environment Connector that provides the communication protocols between Blackboard and the integrated system. Similar to Building Blocks, the Learning Environment Connector provides specific "extension points" for interactions between Blackboard and the integrated system, such as authenticating a session or creating a URL for redirection into an online course.

Benefits of integrating with Blackboard Learn

Single Signon

Users only have to log in to a single application, which provides access across Blackboard and other integrated systems.

The following scenarios are supported by the Learning Environment Connector:

  • User authorization continues to be managed by the Learning Management System
  • User authorization is handled by the Blackboard Learn

Course Information Displayed in One Place

Course and enrollment data is synchronized between Blackboard and the integrated LMS so users can see all of their online courses on a single page. The Learning Environment Connector has interfaces to populate other kinds of portal data, such as announcements or grade data.

Content System

Instructional Designers can use Blackboard Content System files within their LMS courses.

Outcomes System

Outcomes instruments can be deployed to courses in integrated systems.

Terminology

Term Definition
LEC Learning Environment Connector
LMS Learning Management System
AS Academic Suite
Integration A deployed instance of a Learning Environment Connector pointing to an LMS

How does it fit together?

Legend

Learning Environment Connector

Implemented by the third-party developer.  This is a Building Block deployed on the Blackboard server which implements several Java interfaces. This accesses the remote LMS data using a communications protocol supported by the remote LMS (e.g. SOAP, RMI, JDBC, etc.).

Integration Gateway Building Block

Provided by Blackboard.  This implements a data integration API for the Blackboard system and is used by the LMS to notify AS when administrative events occur relating to user/course/enrollment objects.  The API is implemented via HTTP Posts to submit IMS Enterprise XML to Blackboard Learn.

Web UI

Provided by Blackboard.  This provides specific URLs that the LMS can redirect a user to.  Examples include:

  • Redirect the user to the Blackboard Learn login page
  • Perform a single-signon into Blackboard Learn for a user who has already been authenticated by the LMS

Authentication overview

User logging in

User loading an authenticated LMS page

User logging out

Creating a Learning Environment Connector

The following sections outline the process of creating a Learning Environment Connector for your LMS.

Frameset

The LMS must be able to function inside a frameset. Blackboard Learn will load the LMS pages inside a frame called top.content.

Web UI Changes

A number of small changes to the LMS Web UI will be required.  These changes are very LMS-specific. Here are a few examples of the changes made during the process of integrating CE4 and Vista:

  • All HTML references to the frame top are replaced with top.content.
  • Requests for the login page are redirected to AS's login page.
  • A link is added from Vista's File Manager to the Content System file browser.
  • The Logout link is hidden, since Blackboard Learn will manage this operation.

Authentication cookies

The LMS must be able to use cookie-based authentication, and these cookies must be issued for a domain that is common to both the LMS and AS server.

For example:

  • Blackboard Learn server is running on as.mydomain.edu
  • Campus Edition 6 server is running on ce6.mydomain.edu

In this scenario, the authentication cookie for Campus Edition 6 must use the domain .mydomain.edu, so that the Blackboard Learn server can issue cookies on its behalf.

Remote API

The LMS must provide some mechanism for accessing data (both reading and writing) from Java code executing on the Blackboard Learn server. If the LMS has an SDK that supports RPC, this is one way to access the data. Direct database access via JDBC would also be an option.

Communication Requirement

All LMS hardware must be set up to accept COBRA IIOP communication.

This section outlines the process of creating the framework for a simple Learning Environment Connector. Later sections add features to this connector.

Prerequisites

  • Familiarity with the creation of Blackboard Learn Building Blocks.
  • Download the Blackboard Learn Building Block SDK for Release 9.0

Download sample Building Block

The Learning Environment Connector developer package should have included several reference plugins. A sample connector has been prepared based on the vista-extension.zip Connector. This sample can be downloaded here. The steps below will reference this sample.

Prepare the Building Block manifest

A Learning Environment Connector is a special type of Blackboard Learn Building Block. The Building Blocks SDK documentation describes the general structure of a building block. For Learning Environment Connector Building Blocks, there are a few specific things to be modified so it can operate as a Connector instead of as a webapp.

Sample bb-manifest.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<manifest>
  <!-- core extension information -->
  <plugin>
    <name value="Sample Learning Environment Connector" />
    <handle value="sample-ext-plgnhndl" />
    <description value="This plugin provides a Sample Learning Environment Connector" />
    <webapp-type value="javaext" />
    <version value="1.0.0.1" />
    <requires>
      <bbversion value="9.0.0" />
    </requires>
    <vendor>
      <id value="bb" />
      <name value="Blackboard, Inc." />
      <url value="http://www.blackboard.com/" />
      <description value="Blackboard Engineering" />
    </vendor>

    <!-- Extension implementations -->
    <module-defs>
      <definition namespace="blackboard.platform.integration">
        <extension id="sampleIntegrationExtension"
          point="blackboard.platform.integration.integrationExtension"
          class="blackboard.platform.integration.extension.sample.SampleIntegrationExtension"
          singleton="true" />
        <extension id="sampleAuthenticationProvider"
          point="blackboard.platform.integration.authenticationProvider"
          class="blackboard.platform.integration.extension.sample.SampleAuthenticationProvider"
          singleton="false" />
        <extension id="sampleNavigationProvider"
          point="blackboard.platform.integration.navigationProvider"
          class="blackboard.platform.integration.extension.sample.SampleNavigationProvider"
          singleton="false" />
        <extension id="samplePortletProvider"
          point="blackboard.platform.integration.portletProvider"
          class="blackboard.platform.integration.extension.sample.SamplePortletProvider"
          singleton="false" />
        <extension id="sampleSupportProvider"
          point="blackboard.platform.integration.supportProvider"
          class="blackboard.platform.integration.extension.sample.SampleSupportProvider"
          singleton="false" />
      </definition>
    </module-defs>

    <!-- code permissions required for proper operation -->
    <permissions>
      <permission type="runtime" name="getClassLoader" />
      <permission type="runtime" name="createClassLoader" />
      <permission type="java.lang.reflect.ReflectPermission" name="suppressAccessChecks" />
      <permission type="java.net.SocketPermission" name="*" actions="connect,resolve,listen" />
      <permission type="java.io.FilePermission" name="&lt;&lt;ALL FILES&gt;&gt;" actions="read" />
    </permissions>
  </plugin>
</manifest>

Notes about sample

There are a few important entries in this file, which must be present in all Connector implementations:

  • webapp-type: javaext is a new Building Block type added for Blackboard Learn 9.0.  This type provides implementations of Blackboard Learn Java extension points.
  • extension: Each of these elements defines a Java implementation for one of the Connector interfaces.

The following Java extension points are available for Connector implementation:

Extension point Java interface Required for connectors?
blackboard.platform.integration.integrationExtension blackboard.platform.integration.provider.IntegrationExtension mandatory
blackboard.platform.integration.announcementProvider blackboard.platform.integration.provider.AnnouncementProvider optional
blackboard.platform.integration.authenticationProvider blackboard.platform.integration.provider.AuthenticationProvider mandatory
blackboard.platform.integration.contentSystemProvider blackboard.platform.integration.provider.ContentSystemProvider optional
blackboard.platform.integration.migrationProvider blackboard.platform.integration.provider.MigrationProvider optional
blackboard.platform.integration.navigationProvider blackboard.platform.integration.provider.NavigationProvider mandatory
blackboard.platform.integration.portletProvider blackboard.platform.integration.provider.PortletProvider mandatory
blackboard.platform.integration.supportProvider blackboard.platform.integration.provider.SupportProvider mandatory
blackboard.platform.integration.integrationEventListener blackboard.platform.integration.service.IntegrationEventListener optional

Implement IntegrationExtension Java extension

This is the primary entry point into the Learning Environment Connector. This provides Blackboard Learn with server-wide information about the Connector implementation.

Sample implementation

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.extension.AbstractIntegrationExtension;
import blackboard.platform.integration.provider.IntegrationProvider.ProviderType;

import java.util.HashSet;
import java.util.Set;

public class SampleIntegrationExtension extends AbstractIntegrationExtension
{
  private static final Set<String> CONNECTOR_TYPES;
  static
  {
    CONNECTOR_TYPES = new HashSet<String>();
    CONNECTOR_TYPES.add( "SAMPLE" );
  }

  @Override
  public String getProviderId( ProviderType providerType )
  {
    switch ( providerType )
    {
      case Authentication:
        return "sampleAuthenticationProvider";
      case Navigation:
        return "sampleNavigationProvider";
      case Portlet:
        return "samplePortletProvider";
      case Support:
        return "sampleSupportProvider";
      case Announcement:
      case ContentSystem:
      case Migration:
        return null;
    }
    return super.getProviderId( providerType );
  }

  @Override
  public Set<String> getConnectorTypes()
  {
    return CONNECTOR_TYPES;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getConnectorName( String connectorType )
  {
    return "Sample Connector";

  }

  /**
   * {@inheritDoc}
   */
  public Version getVersion()
  {
    return Version.Version1;
  }

  /**
   * {@inheritDoc}
   */
  public void flushConnectorCache()
  {
  }
}

Notes about sample

  • Notice that this extends AbstractIntegrationExtension. It is recommended that all implementations extend this base class to help ensure future binary compatibility. If changes are required in the IntegrationExtension base class, a default implementation will be added to AbstractIntegrationExtension, which ensures the connector continues to function as expected.
  • The strings returned by getProviderId match the extension IDs defined in bb-manifest.xml. When the Building Block is loaded, all extension points defined in the file will be loaded, but they are unused. The return value from getProviderId is how we determine which implementation class to execute.

Implement SupportProvider

Sample implementation

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.extension.AbstractSupportProvider;
import blackboard.platform.integration.provider.SupportProvider;

public class SampleSupportProvider extends AbstractSupportProvider implements SupportProvider
{
  @Override
  public boolean isFeatureSupported( Feature feature )
  {
    return false;
  }
}

Notes about sample

  • Again, notice that this extends AbstractSupportProvider. Each Provider interface has a corresponding abstract implementation which can be used to ensure future binary compatibility.
  • By always returning false from isFeatureSupported, we are indicating that none of the optional Connector features are implemented.

Implement AuthenticationProvider

Sample implementation

package blackboard.platform.integration.extension.sample;

import blackboard.persist.Id;
import blackboard.platform.integration.PasswordChangeForcedException;
import blackboard.platform.integration.PasswordChangeRequiredException;
import blackboard.platform.integration.extension.AbstractAuthenticationProvider;
import blackboard.platform.integration.provider.AuthenticationProvider;

import java.util.Set;

import javax.servlet.http.Cookie;

public class SampleAuthenticationProvider extends AbstractAuthenticationProvider implements AuthenticationProvider
{
  @Override
  public boolean login( Id userId, String password )
    throws PasswordChangeRequiredException, PasswordChangeForcedException
  {
    return false;
  }

  @Override
  public void logout( Id userId )
  {
  }

  @Override
  public Set<Cookie> getCookies( Id userId )
  {
    return null;
  }

  @Override
  public void setCookies( Id userId, Set<Cookie> cookies )
  {
  }

  @Override
  public String getSharedCookieDomain( Id userId )
  {
    return null;
  }

  @Override
  public Set<Id> checkActivity( Set<Id> userIds )
  {
    return null;
  }

  @Override
  public void flushCachedCredentials( Id userId )
  {
  }
}

Implement NavigationProvider

Sample implementation

/*
 * (C) Copyright Blackboard Inc. 1998-2007 - All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software without prior explicit
 * written approval is strictly prohibited. Please refer to the
 * file "copyright.html" for further important copyright and licensing information.
 *
 * BLACKBOARD MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON- INFRINGEMENT.
 * BLACKBOARD SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */
package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.CourseLmsIntegration;
import blackboard.platform.integration.UserLmsIntegration;
import blackboard.platform.integration.extension.AbstractNavigationProvider;
import blackboard.platform.integration.provider.NavigationProvider;

public class SampleNavigationProvider extends AbstractNavigationProvider implements NavigationProvider
{
  @Override
  public String getCourseHome( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getAdminCourseEdit( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getAdminCourseAdd( String returnUrl )
  {
    return "";
  }

  @Override
  public String getAdminCourseEnrollment( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserChangePassword( UserLmsIntegration userLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserChangeExpiredPassword( UserLmsIntegration userLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserForcedChangePassword( UserLmsIntegration userLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getAdminUserAdd( String returnUrl )
  {
    return "";
  }

  @Override
  public String getAdminUserEdit( UserLmsIntegration userLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserEditPersonalInformation( UserLmsIntegration userLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserViewCourseGrades( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    return "";
  }

  @Override
  public String getUserForgotPassword( UserLmsIntegration userLmsInt )
  {
    return "";
  }

  @Override
  public String getUserPortfolio( UserLmsIntegration userLmsInt, String portfolioLcId )
  {
    return "";
  }

  @Override
  public String rewriteUrl( String preexistingUrl )
  {
    return "";
  }
}

Implement PortletProvider

Sample implementation

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.extension.AbstractPortletProvider;
import blackboard.platform.integration.portlet.IntegratedModuleCacheData;
import blackboard.platform.integration.portlet.PortletDataElement;
import blackboard.platform.integration.provider.PortletProvider;

import java.util.*;

public class SamplePortletProvider extends AbstractPortletProvider implements PortletProvider
{
  @Override
  public Set<PortletType> getSupportedPortletTypes()
  {
    Set<PortletType> result = new HashSet<PortletType>();
    return result;
  }

  @Override
  public List<PortletDataElement> getPortalData( PortletType portletType, Map<String, IntegratedModuleCacheData> cache )
  {
    return new ArrayList<PortletDataElement>();
  }
}

Compile the Connector

$ cd build/
$ ant deploy
Buildfile: build.xml

init:

-prepare:

-compile:
     [echo] Compiling classes under C:\cygwin\tmp\lec_sample\sample1\src
    [javac] Compiling 5 source files to C:\cygwin\tmp\lec_sample\sample1\deploy\WEB-INF\classes

-distrib:
      [zip] Building zip: C:\cygwin\tmp\lec_sample\sample1\build\sample-extensions.war

deploy:

BUILD SUCCESSFUL
Total time: 3 seconds

Install the Connector

The sample Connector is now ready for installation. To install it:

  1. Log into Blackboard Learn as an administrator.
  2. Navigate to Admin Panel > Building Blocks > Building Blocks - Installed Tools.
  3. Select Install Building Blocks.
  4. Upload the .war file compiled above.
  5. Return to the Building Blocks - Installed Tools page
  6. Change Availability to Available for the LEC.
  7. Click Approve.

Test the Connector

  1. Log into Blackboard Learn as an administrator
  2. Navigate to Admin Panel > Learning Environment Integrations > Add Integration
  3. Enter the form fields as follows:
    1. Long Name: Sample Integration
    2. Short Name: Sample
    3. Institution Role Name: SampleIntegration
    4. Delegation Priority: 0
    5. Data Source Key: SampleIntegration
    6. Learning Environment Type: Sample Connector
    7. Learning Environment ID: SampleIntegration
    8. SSO Settings: None
    9. Authentication Settings: None
    10. Use Conflict Resolution: Never
  4. Click Submit.

After returning to the Learning Environment Integrations page, you should now see an instance of the integration.

To simplify user deployment of a Learning Environment Connector, it is recommended that the LMS provide a mechanism to export all relevant settings in a single XML file. This file can then be imported into Blackboard Learn during creation of the Integration.

Implementation of this feature is optional. The alternative is to document the values that users should enter for the following fields on the Add Integration page:

  • Learning Environment Type
  • Learning Environment ID
  • SSO Settings
  • Authentication Settings

Settings XML file format

Element Description
IntegrationProperties Root element of file
IntegrationProperties.integrationId Globally unique identifier for remote LMS. This same identifier will be included in all notification sent from the LMS to Blackboard Learn.
IntegrationProperties.targetSystem Learning Environment Connector's identifier. This should match one of the values returned by the Connector's IntegrationExtension.getConnectorTypes method
IntegrationProperties.ssoSettings This is a free-form field that can be used by the LMS to provide any information needed by the Connector implementation.
IntegrationProperties.authenticationSettings This is a free-form field that can be used by the LMS to provide any information needed by the Connector implementation.

Example file

<?xml version="1.0" encoding="utf-8"?>
<IntegrationProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <integrationId>SampleLmsUniqueId</integrationId>
  <targetSystem>SAMPLE</targetSystem>
  <ssoSettings>ssoServerUrl=http://myserver.bbbb.net:8001/webct/ssoServlet; 
    sharedSecret=groucho; frontEndHost=myserver.bbbb.net; frontEndHttpPort=8001; 
    frontEndHttpsPort=8002; frontEndCookieDomain=.bbbb.net</ssoSettings>
  <authenticationSettings>JndiContainerFactoryClass=weblogic.jndi.WLInitialContextFactory;
    jndiProviderUrl=t3://myserver.bbbb.net:8001; am=CsPortalSSOv1AuthenticationModule; 
    instlcid=2044122001; jndiUsername=jndiuser; jndiPassword=webct</authenticationSettings>
</IntegrationProperties>

Testing settings parser

After implementing the settings XML export within the LMS, you will want to test the parsing of this file:

  1. Log into Blackboard Learn as an administrator
  2. Navigate to Admin Panel > Learning Environment Integrations > Add Integration
  3. Fill in the form
    1. Go down to the "Import Settings from File" part of the form, and click Browse to select your exported settings XML file
    2. Click Import Settings

After clicking Import Settings, you should see the following:

  1. Learning Environment Type has been automatically set to Sample Connector
  2. Learning Environment ID matches the <integrationId> value from the XML
  3. SSO Settings matches the <ssoSettings> value from the XML
  4. Authentication Settings matches the <authenticationSettings> value from the XML

Accessing integration settings from within a Connector

The integration settings can be accessed from within any of the Provider classes.

public class SampleAuthenticationProvider extends AbstractAuthenticationProvider implements AuthenticationProvider
{
  @Override
  public boolean login( Id userId, String password )
    throws PasswordChangeRequiredException, PasswordChangeForcedException
  {
    String ssoSettings = getIntegrationContext().getLmsIntegration().getSsoSettings();
    String authSettings = getIntegrationContext().getLmsIntegration().getAuthSettings();

    ...

    return false;
  }

To simplify access to our settings, we'll add a helper method that converts the settings blocks into Maps:

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.service.IntegrationContext;

import java.util.*;

public class SampleSettingsHelper
{
  public static String getSsoSetting( IntegrationContext integrationContext, String settingKey )
  {
    Map<String, String> settings = parseSettingsText( integrationContext.getLmsIntegration().getSsoSettings() );
    return settings.get( settingKey );
  }

  public static String getAuthSetting( IntegrationContext integrationContext, String settingKey )
  {
    Map<String, String> settings = parseSettingsText( integrationContext.getLmsIntegration().getAuthSettings() );
    return settings.get( settingKey );
  }

  static Map<String, String> parseSettingsText( String settingsText )
  {
    Map<String, String> properties = new HashMap<String, String>();

    String[] parameters = settingsText.split( "\\s*;\\s*" );
    if ( null != parameters )
    {
      for ( String parameter : parameters )
      {
        if ( null == parameter )
          continue;

        parameter = parameter.trim();
        if ( parameter.length() == 0 )
          continue;

        String[] parts = parameter.split( "\\s*=\\s*", 2 );
        if ( null == parts || parts.length != 2 )
          continue;

        properties.put( parts[ 0 ], parts[ 1 ] );
      }
    }

    return properties;
  }
}

XML format

The LMS needs to generate an XML snapshot for use in Blackboard Learn. The document used must be well-formed although it does not require DTD validation.
The following elements are require for population of User, Course, and Enrollment information.

Document Header

element name
description
enterprise Packaging element for the data components being provided.

Integration Details

element name description
properties
Responsible for identifying the integration aligned with a 3rd party Learning System.
integrationId GlcId associated with the LmsIntegration record.

Example:

<properties>
  <integrationId>foo</integrationId>
</properties>

Common Element

element name
description
sourcedid
Packaging element for identifying information for the associated entity.
source
Key representing "source" of the data from the 3rd party integration.
id Unique key identifying entity.

User Details

element name
description
person
Packaging element for user information.
recstatus
(optional).  1=add, 2=update, 3=delete.  If not specified, record will be treated as an add if the user doesn't exist yet, and an update if it does exist.
sourcedid See Common element.
userid User name for authentication attempt for user.
n Packaging element for name information.
family Last name for user.
given First name for user.
x_bb_password (optional) User's password.  If specified, this value will be passed to MigrationProvider.convertPassword to have it converted into one of the password types that Bb9 supports.

Example:

<person recstatus="1">
  <sourcedid>
    <source>vendorA</source>
    <id>user_uid</id>
  </sourcedid>
  <userid>username</userid>
  <name>
    <n>
      <family>last name</family>
      <given>first name</given>
    </n>
  </name>
  <extension>
    <x_bb_password>Uw.xDeUgDOwgo</x_bb_password>
  </extension>
</person>

Course Details

element name
description
group Packaging element for course information.
recstatus
(optional).  1=add, 2=update, 3=delete.  If not specified, record will be treated as an add if the course doesn't exist yet, and an update if it does exist.
sourcedid See common element.
description Packaging element for listing information for the course.
short Represents course id attribute.
long Represents title of the associated course.
full Represents the description of the associated course.

Example:

<group recstatus="1">
  <sourcedid>
    <source>vendorB</source>
    <id>course_uid</id>
  </sourcedid>
  <description>
    <short>course id</short>
    <long>course title</long>
    <full>course description</full>
  </description>
</group>

Enrollment Details

element name
description
membership
Packaging element for enrollment information.
sourcedid Identifying information for the associated course record. See common element.
member Packaging element for associated user information.
sourcedid Identifying information for the associated user record. See common element.
role Packaging element for students membership role within the course.
roletype XML enumerated attribute identifying the membership role. See values below.
recstatus
(optional).  1=add, 2=update, 3=delete.  If not specified, record will be treated as an add if the enrollment doesn't exist yet, and an update if it does exist.
role type values
description
0 None
1 Student
2 Instructor
3 Course Builder
6 Teaching Assistant
8 Grader
9 Guest

Example:

<membership>
  <sourcedid>
    <source>vendorB</source>
    <id>course_uid</id>
  </sourcedid>
  <member>
    <sourcedid>
      <source>vendorA</source>
      <id>user_uid</id>
    </sourcedid>
    <role roletype="01" recstatus="1"/>
  </member>
</membership>

Sample integration.xml

<?xml version="1.0" encoding="UTF-8" ?>
<enterprise>
  <properties>
    <integrationId>SampleIntegration</integrationId>
  </properties>
  <person>
    <sourcedid>
      <source>vendorA</source>
      <id>user_uid</id>
    </sourcedid>
    <userid>username</userid>
    <name>
      <n>
        <family>last name</family>
        <given>first name</given>
      </n>
    </name>
  </person>
  <group>
    <sourcedid>
      <source>vendorB</source>
      <id>course_uid</id>
    </sourcedid>
    <description>
      <short>courseid</short>
      <long>course title</long>
      <full>course description</full>
    </description>
  </group>
  <membership>
    <sourcedid>
      <source>vendorB</source>
      <id>course_uid</id>
    </sourcedid>
    <member>
      <sourcedid>
        <source>vendorA</source>
        <id>user_uid</id>
      </sourcedid>
      <role roletype="01"/>
    </member>
  </membership>
</enterprise>

Bulk data export

In addition to the runtime data feed, it is recommended that a mechanism be provided to perform a bulk export of all users, courses, and enrollments on the server. This can then be imported into Blackboard Learn using the snapshot tool. This is useful for LMS servers that already contain records when integration is configured. Existing records will be sent to Blackboard Learn using the bulk export, and any future record events will be sent using the Data Feed.

Windows
cd c:\blackboard\apps\snapshot\bin
snapshot.cmd -f XML_MANUAL_LMS_INT -t c:\data\bulk_snapshot.xml -C ..\data\snapshot.properties
Unix
cd /usr/local/blackboard/apps/snapshot/bin
./snapshot.sh -f XML_MANUAL_LMS_INT -t /usr/local/data/bulk_snapshot.xml -C ../data/snapshot.properties
Note: XML_SNPSHT_LMS_INT is also a valid function to use. XML_MANUAL_LMS_INT ignores any records that are not in the data file but are in the system, while XML_SNPSHT_LMS_INT disables records that are not in the data file but are in the system.

User Merging and Conflict Resolution

In certain cases, users specified in the incoming XML file conflict with users that already exist on the Blackboard Learn server. When this occurs, first determine whether the two users are the same person, using the following rules:

  1. Look for existing integrated users using the IMS SourcedId.source and SourcedId.Id. If an existing record is found using this criteria, the records will be merged using these guidelines:
    • The incoming integration will be added to their list of existing integrations.
    • Authentication will be handled by the integration with the highest delegation priority.
  2. If no integrated user could be found, conflict resolution will be applied and then a new account created:
    • If the integration has been configured to always resolve conflicts, the incoming username will be changed using the rules specified for this integration by the System Administrator.
    • If conflict resolution is only applied when conflicts actually occur, we will check to see whether the incoming IMS SourcedId.Id conflicts with an existing batch_uid, and whether the incoming username conflicts with an existing username.
    • If (after applying conflict resolution) the user account still conflicts with existing records, an error will be thrown.

This section shows how to authenticate a user against a remote LMS.

By the end of this section, you should be able to:

  • Log into Blackboard Learn as an LMS user.
  • Perform SSO as this user from Blackboard Learn into the LMS
  • Log out of the LMS and Blackboard Learn

Prerequisites

It is expected that you have already completed the following parts of the Getting Started guide:

Download sample Building Block

Code for the sample below can be downloaded from here. The steps below reference this sample.

Implement AuthenticationProvider

In the Getting Started section, we created a stub AuthenticationProvider class. Now it is time to expand on this implementation a bit.

The most important methods for user-based operations are login(), getCookies(), and logout(). Our sample below focuses on these methods. The Javadoc for AuthenticationProvider provides more information about each of these methods.

Sample implementation

public class SampleAuthenticationProvider extends AbstractAuthenticationProvider implements AuthenticationProvider
{
  @Override
  public boolean login( Id userId, String password )
    throws PasswordChangeRequiredException, PasswordChangeForcedException
  {
    String lmsUserName = getIntegrationContext().getUserIntegration( userId ).getLmsUsername();

    // Simple hack to allow testing with the demo user
    if ( "username".equals( lmsUserName ) )
    {
      if ( "password".equals(password))
        return true;
      else if ( "changeRequired".equals(password))
        throw new PasswordChangeRequiredException( "Password has expired." );
      else if ( "changeForced".equals(password))
        throw new PasswordChangeForcedException( "Password must be changed on first login." );
      else
        return false;
    }

    //
    // You will need to provide a real method to contact the LMS here.  The following
    // is an example of how a fictional LMS might perform this action.
    //
    // Note: When implementing the real version, security concerns must be taken into account.
    //

    // Send a request to the LMS's authenticateUser SDK method, with the username and password
    // to validate.  The LMS will return a string indicating that the password was valid,
    // was valid and has expired, or was invalid.

    NameValuePair[] parameters = { new NameValuePair( "username", lmsUserName ),
                                   new NameValuePair( "password", password ) };
    try
    {
      String response = sendSecureRequest( "sdk/authenticateUser", parameters );
      if ( "true".equalsIgnoreCase( response ) )
        return true;
      else if ( "PasswordChangeRequired".equalsIgnoreCase( response ) )
        throw new PasswordChangeRequiredException( "Password has expired." );
      else if ( "PasswordChangeForced".equalsIgnoreCase( response ) )
        throw new PasswordChangeForcedException( "Password must be changed on first login." );
      else
        return false;
    }
    catch ( IOException e )
    {
      LogServiceFactory.getInstance().logError( "Unexpected exception", e );
      return false;
    }
  }

  @Override
  public void logout( Id userId )
  {
    String lmsUserName = getIntegrationContext().getUserIntegration( userId ).getLmsUsername();

    // Simple hack to allow testing with the demo user
    if ( "username".equals( lmsUserName ) )
    {
      return;
    }

    //
    // You will need to provide a real method to contact the LMS here.  The following
    // is an example of how a fictional LMS might perform this action.
    //
    // Note: When implementing the real version, security concerns must be taken into account.
    //

    // Send a request to the LMS's logoutUser SDK method, with the username of the user
    // to log out.  In this example, a shared secret is also being sent to prove that
    // this server is allowed to terminate the user's session.

    String sharedSecret = SampleSettingsHelper.getAuthSetting( getIntegrationContext(), "sharedSecret" );
    NameValuePair[] parameters = { new NameValuePair( "username", lmsUserName ),
                                   new NameValuePair( "sharedSecret", sharedSecret ) };
    try
    {
      sendSecureRequest( "sdk/logoutUser", parameters );
    }
    catch ( IOException e )
    {
      LogServiceFactory.getInstance().logError( "Unexpected exception", e );
    }
  }

  @Override
  public Set<Cookie> getCookies( Id userId )
  {
    String lmsUserName = getIntegrationContext().getUserIntegration( userId ).getLmsUsername();
    Set<Cookie> cookieList = new HashSet<Cookie>();

    // Simple hack to allow testing with the demo user
    if ( "username".equals( lmsUserName ) )
    {
      return cookieList;
    }

    //
    // You will need to provide a real method to contact the LMS here.  The following
    // is an example of how a fictional LMS might perform this action.
    //
    // Note: When implementing the real version, security concerns must be taken into account.
    //

    // Send a request to the LMS's getUserCookie SDK method, with the username of the
    // to get the cookie for, along with a shared secret to prove that this server is
    // allowed to request the user's cookie.  The LMS will return a specially encoded
    // string which we can transform into a cookie on our end.
    //
    // An alternate way to perform this action would be to have the LMS include the
    // user's cookie in the response headers.  This is how the Vista Connector is
    // implemented.

    String sharedSecret = SampleSettingsHelper.getAuthSetting( getIntegrationContext(), "sharedSecret" );
    String cookieDomain = SampleSettingsHelper.getAuthSetting( getIntegrationContext(), "cookieDomain" );

    NameValuePair[] parameters = { new NameValuePair( "username", lmsUserName ),
                                   new NameValuePair( "sharedSecret", sharedSecret ) };
    try
    {
      String cookieString = sendSecureRequest( "sdk/getUserCookie", parameters );
      Map<String, String> cookieParts = SampleSettingsHelper.parseSettingsText( cookieString );

      Cookie cookie = new Cookie( cookieParts.get( "key" ), cookieParts.get( "value" ) );
      cookie.setDomain( cookieDomain );
      cookie.setMaxAge( -1 );
      cookie.setPath( "/" );
      cookieList.add( cookie );
    }
    catch ( IOException e )
    {
      LogServiceFactory.getInstance().logError( "Unexpected exception", e );
    }

    // TODO - cache cookies internally so that we don't have to contact the LMS repeatedly

    return cookieList;
  }

  @Override
  public void setCookies( Id userId, Set<Cookie> cookies )
  {
    // TODO - cache cookies internally, and update the cache here
  }

  @Override
  public String getSharedCookieDomain( Id userId )
  {
    return SampleSettingsHelper.getAuthSetting( getIntegrationContext(), "cookieDomain" );
  }

  @Override
  public Set<Id> checkActivity( Set<Id> userIds )
  {
    // TODO - check for activity on remote LMS
    return null;
  }

  @Override
  public void flushCachedCredentials( Id userId )
  {
    // TODO - cache cookies internally, and flush the cache here
  }

  /**
   * Sends an https request to the remote LMS, and returns the LMS's response as a string.
   */
  private String sendSecureRequest( String targetUrl, NameValuePair[] parameters ) throws IOException
  {
    String lmsHostname = SampleSettingsHelper.getAuthSetting( getIntegrationContext(), "lmsHostname" );
    PostMethod httpPost = new PostMethod( "https://" + lmsHostname + "/" + targetUrl );
    httpPost.setRequestBody( parameters );

    try
    {
      int statusCode = new HttpClient().executeMethod( httpPost );
      if ( statusCode != HttpStatus.SC_OK )
      {
        LogServiceFactory.getInstance().logError( "Request failed: statusCode=" + statusCode );
        return null;
      }

      return httpPost.getResponseBodyAsString();
    }
    finally
    {
      httpPost.releaseConnection();
    }
  }
}

Testing the sample

  1. Create a new integration instance for your LMS server.
  2. Import an XML snapshot containing one or more test users.
  3. Navigate to the Blackboard Learn login page.
  4. Log in as user username with password password.

All outgoing links from Blackboard Learn to the LMS are managed by the NavigationProvider implementation.

Prerequisites

It is expected that you have already completed the Learning Environment Connector Developer Guide - User Authentication section and are able to log in as the sample user.

Download sample Building Block

Code for the sample below can be downloaded from here. The steps below reference this sample.

Implement NavigationProvider

Sample implementation

The following is a simple example which implements only the core methods. As more SupportProvider Feature codes are enabled, the corresponding NavigationProvider methods must also be implemented.

public class SampleNavigationProvider extends AbstractNavigationProvider implements NavigationProvider
{
  @Override
  public String getCourseHome( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    String target = "courseHome for course imsId=" + courseLmsInt.getSourcedidId() + "; imsSource="
                    + courseLmsInt.getSourcedidSource() + "; returnUrl=" + returnUrl;
    return "http://www.google.ca/search?q=" + UrlUtil.encodeUrl( target );
  }

  @Override
  public String getUserForgotPassword( UserLmsIntegration userLmsInt )
  {
    String target = "userForgotPassword for lmsUserName=" + userLmsInt.getLmsUsername();
    return "http://www.google.ca/search?q=" + UrlUtil.encodeUrl( target );
  }

  @Override
  public String getUserViewCourseGrades( CourseLmsIntegration courseLmsInt, String returnUrl )
  {
    String target = "userViewCourseGrades for course imsId=" + courseLmsInt.getSourcedidId() + "; imsSource="
                    + courseLmsInt.getSourcedidSource() + "; returnUrl=" + returnUrl;
    return "http://www.google.ca/search?q=" + UrlUtil.encodeUrl( target );
  }

  @Override
  public String rewriteUrl( String preexistingUrl )
  {
    return preexistingUrl;
  }
}

Testing the sample

It is assumed that you have already created the sample integration:

  1. Navigate to the Blackboard Learn login page.
  2. Log in as user username with password password.
  3. Click on the Courses tab.
  4. Click the courseid: course title course.

Each integration has an Blackboard Learn tab associated with it. This tab contains a number of modules which are generated by the Learning Environment Connector's PortletProvider implementation.

Prerequisites

It is expected that you have already completed the Learning Environment Connector Developer Guide - User Authentication section and are able to log in as the sample user.

Download sample Building Block

Code for the sample below can be downloaded from here. The steps below reference this sample.

Implement PortletProvider

Sample implementation

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.extension.AbstractPortletProvider;
import blackboard.platform.integration.portlet.IntegratedModuleCacheData;
import blackboard.platform.integration.portlet.PortletDataElement;
import blackboard.platform.integration.provider.PortletProvider;

import java.util.*;

public class SamplePortletProvider extends AbstractPortletProvider implements PortletProvider
{
  @Override
  public Set<PortletType> getSupportedPortletTypes()
  {
    Set<PortletType> result = new HashSet<PortletType>();
    result.add( PortletType.CourseList );
    result.add( PortletType.MySettings );
    return result;
  }

  @Override
  public List<PortletDataElement> getPortalData( PortletType portletType, Map<String, IntegratedModuleCacheData> cache )
  {
    switch ( portletType )
    {
      case CourseList:
        return getCourseList();
      case MySettings:
        return getMySettings();
      default:
        return new ArrayList<PortletDataElement>();
    }
  }

  private List<PortletDataElement> getCourseList()
  {
    List<PortletDataElement> result = new ArrayList<PortletDataElement>();

    //
    // If information from the LMS is needed to build this module,
    // this is where you would load that information
    //

    PortletDataElement course1 = new PortletDataElement();
    course1.setDisplayText( "Course 1 (unavailable)" );
    result.add( course1 );

    PortletDataElement course2 = new PortletDataElement();
    course2.setDisplayText( "Course 2" );
    course2.setLinkUrl( "http://www.google.ca/search?q=course2" );
    course2.setLinkText( "link to course2" );
    result.add( course2 );

    return result;
  }

  private List<PortletDataElement> getMySettings()
  {
    List<PortletDataElement> result = new ArrayList<PortletDataElement>();

    //
    // If information from the LMS is needed to build this module,
    // this is where you would load that information
    //

    PortletDataElement setting1 = new PortletDataElement();
    setting1.setDisplayText( "Setting 1" );
    setting1.setLinkText( "Setting 1" );
    setting1.setLinkUrl( "http://www.google.ca/search?q=setting1" );
    result.add( setting1 );

    PortletDataElement setting2 = new PortletDataElement();
    setting2.setDisplayText( "Setting 2" );
    setting2.setLinkText( "Setting 2" );
    setting2.setLinkUrl( "http://www.google.ca/search?q=setting2" );
    result.add( setting2 );

    return result;
  }
}

Testing the sample

It is assumed that you have already created the sample integration:

  1. Navigate to the Blackboard Learn login page.
  2. Log in as user username with password password.
  3. Click the Sample tab.

On this tab, you should see the following modules:

  • My Features: Sample
  • Course List: Sample
The implementation of this feature is optional. Can use the snapshot XML tool from the command-line.

XML feeds can be submitted directly to the server via the Integrated Learning System Gateway plugin.  This building block has to be configured before it can be used.  Specifically, the Shared Secret setting must be provided to enable request authentication. See Learning Environment Connector Developer Guide - Snapshot XML for more details.

XML feed notes

  • URL is /webapps/bb-integration-gateway-bb_bb60/xml-feed
  • Parameters
    • timestamp
    • mac
    • XML file
      • The contents of this file should be used when generating the MAC.
      • The name of the file should be used when sorting the list of parameters.
      • This file may be up to 10MB, but we recommend that you keep it smaller. Our internal implementations limit this to 100 changes per file.
  • LMS is expected to perform a multi-part POST

The MAC provided is compared to that calculated by Blackboard Learn code.

MAC Generation Algorithm

  1. Sort the parameters alphabetically by parameter name. The sort is performed without regard to case sensitivity.
  2. Concatenate the parameters used in the auth request (GLCID, timestamp, etc) into a single string.
  3. Append the Shared Secret to the string.
  4. Encrypt the string in 16 byte string using MD5 Algorithm, using UTF-8 encoding to convert the string to bytes.
  5. Convert the 16-byte string into a 32-byte alphanumeric (HEX) string to make it URL-friendly.

MAC Generation for Runtime Sync

The MAC is calculated using the algorithm described above.  The fields to include in this calculation are:

  • timestamp
  • XML file
    • This multi-part element should contain a filename.  The filename will be used as the parameter name when sorting parameters for MAC generation.

Integration Response

The synch is the processed within Bb and status provided to the caller. The response is in XML format.

<!ELEMENT response (status, errorlist?)>
<!ELEMENT status (#PCDATA)>

<!ELEMENT errorlist (error*, person*, group*, enrollment*)>
<!ELEMENT error (#PCDATA)>
<!ELEMENT person (source,id,error*)>
<!ELEMENT group (source,id,error*)>
<!ELEMENT enrollment (group_source_id,person_source_id,error*)>
<!ELEMENT source (#PCDATA)>
<!ELEMENT id (#PCDATA)>
<!ELEMENT group_source_id (#PCDATA)>
<!ELEMENT person_source_id (#PCDATA)>

Samples

Sucess Example
<RESPONSE>
  <STATUS>
    Success
  </STATUS>
</RESPONSE>
Error example
<RESPONSE>
  <STATUS>
    Error
  </STATUS>
  <ERRORLIST>
    <ERROR>
      Exception.
    </ERROR>
    <PERSON>
      <SOURCE>
        user source
      </SOURCE>
      <ID>
        user unique id
      </ID>
      <ERROR>
        failure
      </ERROR>
    </PERSON>
    <GROUP>
      <SOURCE>
        group source
      </SOURCE>
      <ID>
        group unique id
      </ID>
      <ERROR>
        failure
      </ERROR>
    </GROUP>
    <ENROLLMENT>
      <GROUP_SOURCE_ID>
        group unique id
      </GROUP_SOURCE_ID>
      <PERSON_SOURCE_ID>
        user unique id
      </PERSON_SOURCE_ID>
      <ERROR>
        failure
      </ERROR>
    </ENROLLMENT>
  </ERRORLIST>
</RESPONSE>

The LMS should be modified to notify Blackboard Learn whenever an administrative event occurs relating to users, courses, and enrollments. The implementation on the LMS-side is vendor-specific. Some possible mechanisms include:

Asynchronous Queue

An asynchronous queue can be built which holds all pending adds, updates, and deletes for these record types. Periodically a background job can then assemble these records into IMS Enterprise XML and send the events to the Integration Gateway Building Block.

This assumes that there is some mechanism for launching background jobs. If this mechanism doesn't exist, a simple solution could be to use a Unix cron daemon or a Windows Task Scheduler.

Synchronous Notification

With this mechanism, there is some form of listener watching for user/course/enrollment events. This listener would immediately send the event through to the Integration Gateway Building Block.

One problem with this solution is that it does not deal well with failures on the Blackboard Learn side. If the Blackboard Learn server is down, the event could be lost, leading to inconsistent data.

Mechanism to suspend data feed

It is recommended that the LMS administrator have a way to suspend the integration gateway data feed. For the CE4 and Vista implementations, we provide System Administrators with the following options:

  • Enable run-time synchronization
  • Enable run-time synchronization event capturing

One use case where this might be useful is when a very large enterprise import is expected on the LMS. The administrator would do the following:

  1. Disable run-time synchronization.
  2. Perform the bulk import into the LMS.
  3. Perform a bulk export in Blackboard Learn snapshot format, and import the snapshot into Blackboard Learn.
  4. Enable run-time synchronization.

Integration with the Outcomes System

Prerequisites

  • Outcomes is enabled.
  • Integration is set up (eg. Vista Sections are available and there are users attached to them).
  • Person records are created and they are linked to the integrated users.

Integration Steps

  1. Login to Blackboard Learn as System Administrator.
  2. Click the Outcomes tab and Discover.
  3. Click Courses.
  4. Add a course:
    1. Click Course Sections
    2. Add a section by entering the necessary information
    3. Browse to an integrated Course for the Learning System Course ID
    4. Click Submit
    5. Click Edit on the newly created section.
      1. Click Affiliations to add an affiliation or perform a batch add.
      2. Choose(or use) person records from the integrated system.
  5. Go back to the Course and add a Survey:
    1. Enable the Public and Allow Deployment options
    2. Set the status to Complete.
    3. Go to Style and add a few questions to the survey
    4. Click Submit.
    5. Go to Deployments > Add Deployment.
    6. Fill in the necessary information.
    7. Choose Select Affiliations and then choose Distribute to all persons directly affiliated with this Place and Distribute to all persons affiliated with this Unit.
    8. Select Course Announcement and/or System Announcement and/or Email deployment notification.
    9. Start Deployment

Sample AnnouncementProvider Implementation:

SampleAnnouncementProvider.java
package blackboard.platform.integration.extension.sample;

import blackboard.persist.Id;
import blackboard.platform.integration.IntegrationException;
import blackboard.platform.integration.exchange.AnnouncementXO;
import blackboard.platform.integration.extension.AbstractAnnouncementProvider;
import blackboard.platform.integration.provider.AnnouncementProvider;

public class SampleAnnouncementProvider extends AbstractAnnouncementProvider implements AnnouncementProvider
{

  @Override
  public void createAnnouncement( AnnouncementXO announcementXO ) throws IntegrationException
  {
    if ( announcementXO.getParentCourseLmsInt() != null )
    {
        Id courseId = announcementXO.getParentCourseLmsInt().getCourseId();
        announcementXO.setExtCourseId( courseId );
    }

    long announcmentId = -1L;
    //
    // You will need to provide a real method to contact the LMS here. The following
    // is an example of how a fictional LMS might perform this action.
    //
    //    AnnouncementSDK annSDK = new AnnouncementSDK( props );
    //    announcementId = annSDK.createAnnouncement( sessionVO, announcementVo )

    announcementXO.setExtAnnouncementId( announcmentId );

  }

  @Override
  public void deleteAnnouncement( AnnouncementXO announcementXO ) throws IntegrationException
  {
      //
      // You will need to provide a real method to contact the LMS here. The following
      // is an example of how a fictional LMS might perform this action.
      //
      //    AnnouncementSDK annSDK = new AnnouncementSDK( props );
      //    annSDK.deleteAnnouncement( sessionVO, delAnnVo );
  }

}

Outcomes Integration Notes

  • You can call /webapps/blackboard/execute/getLicense?cmd=isOutcomesSystemLicensed to determine whether Outcomes is licensed in Blackboard Learn.
  • Outcomes Objectives
    • This is a lightweight Blackboard Learn tool that can run within the LMS's frameset.
    • URL: /webapps/blackboard/execute/getCourseObjectives.
    • Parameters:
      • id - Course's IMS Sourcedid.Id
      • name - Course's IMS Sourcedid.Source

Integration with the Blackboard Learn Content System includes modifying the availability of the Content Provider to the LMS, browsing for items within the Blackboard Learn Content System, and the request of content material. Browsing/Serving/Posting is accomplished via the Integrated Learning System Gateway plugin which must be available for proper handling.

Content System Event Notification

  • ContentSystemProvider
    • Can be optionally implemented.
    • Pertinent for management for Content System resources. The only method currently employed controls notification of the availability of the Content System.
    • When it is disabled within Blackboard Learn a notification can then be consumed by the LMS to control viewing of browsing/navigation components that it contains.
    • Validate that the Content System is supported:
      • You can call /webapps/blackboard/execute/getLicense?cmd=isContentSystemLicensed to determine whether Content System is licensed in Blackboard Learn
      • Whenever Content System is enabled or disabled, ContentSystemProvider.updateContentSystemAvailability will be called.

Content System File Browsing

  • Accessing file picker:
    • Direct user's browser to /webapps/bb-integration-gateway-bb_bb60/cs-file-browser?glcId=<integrationglcId> from inside a frameset.
    • One of the parent frames must define the function (CSAddCallBack), which will be used as a Javascript callback.
  • Parent frameset design
    • Window name is required to be filePickerFrame.
    • CSAddCallBack javascript function is required for appropriately transmitting the information back to the client LMS for persistence.
      function CSAddCallBack(results)
      {
        for ( i =0; i < results. length ; i \+\+ )
        {
           retVals = results\[i\];
           alert(retVals.id \+"\n" + retVals.name \+"\n" + retVals.type );
        }
      }
      

      retVals.id: Unique key to identify the content.
      retVals.name: Actual name of the content item.
      retVals.type: Represents File or Folder as types of content available.

    • Include window domain name setting to allow for cross-scripting security restraints.

Content System File Rendering

  • Only supports read-only access to Content System. The LMS can load files from Content System but cannot save files to there.
  • Render a Content System file:
    • To load the file body, send HTTP GET (or multipart POST) to /webapps/bb-integration-gateway-bb_bb60/cs-file.  Use the following parameters:
    • Security model
      • No permission checks are performed by this request, since the file is being retrieved by the server rather than by the user's browser.  It is expected that the LMS will perform its own authorization checks before loading the content.

The Learning Environment Connector API includes partial support for user and course conversion operations. This feature is aimed at CE4, CE6, and Vista installations, and it is unlikely that it will be used by third-party implementations.

Download sample Building Block

Code for the sample below can be downloaded from here. The steps below reference this sample.

User Conversion

To add support for this feature, you need to do the following:

  • Add Feature.ConvertUser to the list of valid Feature codes SupportProvider.isFeatureSupported() returns true for
  • Implement the MigrationProvider interface
  • Implement the MigrationProvider.convertPassword, MigrationProvider.getUserPasswords, and MigrationProvider.markUsersConverted methods

On the LMS side, the following requirements must be met:

  • User password hashes must be compatible with one of the formats supported by Blackboard Learn. If no compatible format is available, user conversion can still be performed, but user passwords must be reset afterwards. Formats currently supported:
    • MD5 crypt
    • DES crypt

Course Conversion

Supporting this feature requires that the LMS is capable of producing Blackboard Learn-compatible content packages.

To add support for this feature, you need to do the following:

  • Add Feature.ConvertCourse to the list of valid Feature codes SupportProvider.isFeatureSupported() returns true for
  • Implement the MigrationProvider interface
  • Implement the MigrationProvider.convertCourse, MigrationProvider.exportCourse, and MigrationProvider.undoCourseConversion methods

On the LMS side, the following requirements must be met:

  • Converted courses should not be available to users, since they are no longer the authoritative instance of that course.
  • Content packages must be in Blackboard Learn format (Blackboard Learn supports IMS Content Packaging Specification v1.1.2).

Sample Code

bb-manifest.xml changes

<?xml version="1.0" encoding="ISO-8859-1"?>
<manifest>
    ...
    <module-defs>
      <definition namespace="blackboard.platform.integration">
        ...
        <extension id="sampleMigrationProvider"
          point="blackboard.platform.integration.supportProvider"
          class="blackboard.platform.integration.extension.sample.SampleMigrationProvider"
          singleton="false" />
        ...
      </definition>
    </module-defs>
    ...
</manifest>

IntegrationExtension changes

public class SampleIntegrationExtension extends AbstractIntegrationExtension
{
  ...

  @Override
  public String getProviderId( ProviderType providerType )
  {
    switch ( providerType )
    {
      ...
      case Migration:
        return "sampleMigrationProvider";
      ...
    }
    ...
  }

  ...
}

MigrationProvider implementation

package blackboard.platform.integration.extension.sample;

import blackboard.platform.integration.CourseLmsIntegration;
import blackboard.platform.integration.exchange.CourseContentPackageXO;
import blackboard.platform.integration.exchange.UserPasswordXO;
import blackboard.platform.integration.extension.AbstractMigrationProvider;
import blackboard.platform.log.Log;

import java.io.InputStream;
import java.util.*;

public class SampleMigrationProvider extends AbstractMigrationProvider
{
  @Override
  public UserPasswordXO convertPassword( String lmsPassword )
  {
    //
    // This method will need to transform encrypted passwords from the LMS's
    // hashing format into one of the formats supported by Blackboard Learn.
    // If no compatible format is available, you can return UNSET_PASSWORD,
    // which means the user must reset their password after conversion.
    //
    return UserPasswordXO.UNSET_PASSWORD;
  }

  @Override
  public Map<String, UserPasswordXO> getUserPasswords( Set<String> lmsUserNames, Log auditLog )
  {
    //
    // This method will need to contact the LMS to acquire the current passwords
    // for the specified users.  These passwords can then be transformed into
    // one of the formats supported by Blackboard Learn.
    //

    Map<String, UserPasswordXO> result = new HashMap<String, UserPasswordXO>();
    for ( String lmsUserName : lmsUserNames )
    {
      result.put( lmsUserName, UserPasswordXO.UNSET_PASSWORD );
    }

    return result;
  }

  @Override
  public Map<String, Boolean> markUsersConverted( Set<String> lmsUserNames, boolean isConverted, Log auditLog )
  {
    //
    // This method will need to notify the LMS that the users have been successfully
    // converted, and are now native Blackboard Learn users.
    //

    Map<String, Boolean> result = new HashMap<String, Boolean>();
    for ( String lmsUserName : lmsUserNames )
    {
      result.put( lmsUserName, Boolean.TRUE );
    }

    return result;
  }

  @Override
  public CourseContentPackageXO convertCourse( CourseLmsIntegration courseIntegration )
  {
    //
    // This method will need to do the following:
    //   1. Make the LMS instance of the course unavailable in the UI
    //   2. Export a Blackboard Learn-compatible content package from the LMS course
    //   3. Make the content package available to Blackboard Learn in the form of
    //      an input stream.
    //

    InputStream packageStream = null; // TODO - acquire a valid input stream from the LMS
    String packageName = "mypackage.zip";

    return new CourseContentPackageXO(packageStream, packageName);
  }

  @Override
  public void undoCourseConversion( CourseLmsIntegration courseIntegration )
  {
    //
    // Notify the LMS that the conversion operation failed, and that any
    // changes made to support conversion should be rolled back.
    //
  }

  @Override
  public CourseContentPackageXO exportCourse( CourseLmsIntegration courseIntegration )
  {
    //
    // This method is very similar to convertCourse; it will need to do the following:
    //   1. Export a Blackboard Learn-compatible content package from the LMS course
    //   2. Make the content package available to Blackboard Learn in the form of
    //      an input stream.
    //

    InputStream packageStream = null; // TODO - acquire a valid input stream from the LMS
    String packageName = "mypackage.zip";

    return new CourseContentPackageXO(packageStream, packageName);
  }

  @Override
  public boolean isPublisherCourse( CourseLmsIntegration courseIntegration )
  {
    //
    // If the LMS supports the concept of publisher-protected content, this is where
    // Blackboard Learn can be notified whether the current course contains any
    // of this publisher content.
    //

    return false;
  }
}

Test the Connector

User conversion
  1. Log into Blackboard Learn as an System Administrator.
  2. Navigate to Admin Panel > Learning Environment Integrations.
  3. Click the context menu for one of your existing integrations, and select Convert Users.
  4. Select one or more users for conversion.
  5. Click Convert.
Course conversion
  1. Navigate to Admin Panel > Learning Environment Integrations.
  2. Click the context menu for one of your existing integrations, and select Convert Courses.
  3. Select one or more courses for conversion.
  4. Click Convert.
License check for content system
%AS_SERVER_URL%/webapps/blackboard/execute/getLicense?cmd=isContentSystemLicensed

License check for outcome system
%AS_SERVER_URL%/webapps/blackboard/execute/getLicense?cmd=isOutcomesSystemLicensed
Content browser file picker
%AS_SERVER_URL%/webapps/bb-integration-gateway-bb_bb60/cs-file-browser?glcId=xxx
Note: glcId=>(integration Id)
Conversion in progress page:
%AS_SERVER_URL%/webapps/blackboard/admin/lms_course_conversion_in_progress.jsp
Getting contents from xythos
%AS_SERVER_URL%/webapps/bb-integration-gateway-bb_bb60/cs-file?timestamp=xxx&mac=xxx&id=xxx&integrationId=xxx
Note: mac=>(macV2) id=>(xythosId), integrationId=>(integrationId)
Change event xml feed:
%AS_SERVER_URL%/webapps/bb-integration-gateway-bb_bb60/xml-feed + MultipartPostMethod
Post includes: FilePart(feedfile) + StringPart("timestamp", timestamp) + StringPart("mac", macV2)
Single sign on URL:
%AS_SERVER_URL%/webapps/bb-integration-gateway-bb_bb60/sso?username=xxx&integrationId=xxx&targetLocation=xxx&mac=xxx
Note: username=>(userId), integrationId=>(integrationId), targetLocation=>(GatewayTargetLocation), mac=>(macV2)
GatewayTargetLocation can be one of the following ways:
/cobaltMainFrame.dowebct?appforward=/webct/viewMyWebCT.dowebct",
/entryPageIns.dowebct",
/logonDisplay.dowebct",
/cobaltMainFrame.dowebct?appforward=/webct/startFrameSet.dowebct%3Fforward=manageCourse.dowebct",
/webapps/blackboard/execute/launcher"
Outcome objectives:
%AS_SERVER_URL%/webapps/blackboard/execute/getCourseObjectives?id=xxx&name=xxx
id=>(sectionSourceId) name=>(sectionSourceName)
AS Server url:
(Secure protocal)%AS_SERVER_URL%/

Normally authentication is handled by the Blackboard Learn server, delegating the password validation request to the LMS when necessary. In certain cases it may be necessary for the LMS to automatically log a user into Blackboard Learn without requiring the user to enter their password. An example of this is when the LMS supports Single Signon from an external portal. If the LMS has already handled user validation, it may use Blackboard Learn's Single Signon mechanism to redirect the user into Blackboard Learn, bypassing the user login page:

!Learning Environment Connector Developer Guide^sequence_sso.png!

Implementing Single Signon

Single Signon is handled by the Integration Gateway Building Block. As with Runtime Sync and Content System integration, this Building Block must be configured with a shared secret.

The Single Signon request should be sent to the following URL on the Blackboard Learn server: /webapps/bb-integration-gateway-bb_bb60/sso. Parameters to include in the request:

Specifying a page to redirect to once authenticated

After the user has been authenticated within Blackboard Learn, the Single Signon mechanism supports redirecting the user to an arbitrary location. This is accomplished by providing a value for the targetLocation parameter. After the SSO request has been authenticated, the user will be redirected into the integration's tab. If targetLocation has been specified, that URL will be loaded in the tab's frame.

If necessary, the targetLocation URL can be modified before it is loaded for the user. This is accomplished by implementing the NavigationProvider.rewriteUrl(String) method.

Example URLs

The shared secret used to generate the following examples is "demo".

Without a targetLocation value:

[http://tick.bbbb.net/webapps/bb-integration-gateway-bb_bb60/sso?username=testuser&integrationId=myintegrationid&targetLocation=]

With a targetLocation value:

[http://tick.bbbb.net/webapps/bb-integration-gateway-bb_bb60/sso?username=testuser&
  integrationId=myintegrationid&targetLocation=/lms/content/here.html&mac=05e6ea29e3fe7536f62c30d1358400b4]

The API provides a mechanism for notifying the Connector implementation of several different event types:

  • Integration instance is deleted
  • User is removed from an integration instance

If the Connector needs to take action based on these events, an implementation of the IntegrationEventListener can be provided.

Download sample Building Block

Code for the sample below can be downloaded from [here|Learning Environment Connector Developer Guide^sample-6.zip]. The steps below reference this sample.

Sample code

bb-manifest.xml changes

<?xml version="1.0" encoding="ISO-8859-1"?>
<manifest>
    ...
    <module-defs>
      <definition namespace="blackboard.platform.integration">
        ...
        <extension id="sampleIntegrationEventListener"
          point="blackboard.platform.integration.integrationEventListener"
          class="blackboard.platform.integration.extension.sample.SampleIntegrationEventListener"
          singleton="true" />
        ...
      </definition>
    </module-defs>
    ...
</manifest>

IntegrationExtension implementation

package blackboard.platform.integration.extension.sample;

import blackboard.persist.Id;
import blackboard.platform.integration.IntegrationException;

public class SampleIntegrationEventListener extends AbstractIntegrationEventListener
{
  @Override
  public void integrationDeleted( Id id ) throws IntegrationException
  {
    //
    // This integration instance has been deleted.  This is a good time to
    // clean up any cached data related to the integration.
    //
  }

  @Override
  public void userDeintegrated( Id id ) throws IntegrationException
  {
    //
    // This user has been removed from one or more integration instances.
    // This is a good time to clean up any cached data related to the user.
    //
  }
}
Adaptavist Theme Builder (4.1.3) Powered by Atlassian Confluence 3.3, the Enterprise Wiki