| Blackboard Learn includes a sample Proxy Tool Server Application written in Java to demonstrate all of the interactions between the Proxy Tool and Blackboard Learn. This documentation references the source files in the sample application; prepare the sample application development environment to get the most out of this documentation.
The same concepts apply when writing your proxy tool server in any other language. Integration between the proxy server and Blackboard Learn is accomplished by Web Service calls, an XML description of the service, and a few simple-xml-response web requests. |
High Level Installation Flow
- Your application will ask the user for the URL of the Blackboard Learn server and the optional tool registration password as well as a separate shared secret for this Blackboard Learn server inside your application. The new installation flow involves getting information such as the URL for the server from the Tool Consumer Profile.
- Your application will then call the ContextWS.registerTool method with your application's XML descriptor of your proxy tool.
- The Blackboard Learn System Administrator will then login to their system and make the tool 'available'. They will also set the shared secret on the webservice agent for your tool - this will enable tool specific logins.
Runtime Flow
- A Blackboard Learn user clicks a link which is the passthrough to the tool.
- If you have configured a Ping request then Blackboard Learn sends a simple-xml-response to confirm that the tool is active. If the ping request fails then the user is directed to an error page.
- The user's browser will then POST all the arguments applicable to the specific request as well as a MAC (see Proxy Tool SSO) to the proxy tool's configured URL.
- The proxy tool is responsible for validating the MAC with all of the posted arguments. This is to assure that the parameters received are correct (i.e. the user has not intercepted at the browser and changed anything).
- Continue as appropriate for the touch point. (Refer to the XML description for details on what to expect at each touch point.) Refer to Maintaining Look-and-Feel inside Proxy Server for directions on how to get an appropriate stylesheet for your pages.
Web Service Support
There are two modes of Web Service operation that can be used in the proxy tool:
- Connect as the Blackboard Learn user currently logged in
- Connect as 'your tool'
If you wish to perform actions on behalf of the current user then use the first approach. If you need to perform raised-privilege actions (i.e. update the Grade Center with a student's mark) then use your tool's authentication. One of the SSO parameters passed through each touch point is a 'ticket' for the current user. This can be used in the ContextWS.ticketLogin() method to authenticate a webservice session for the current user.
Refer to the sample proxy server code for a recommended implementation of the authentication logic for a request to your proxy server from Blackboard Learn (or from the browser as redirected by Blackboard Learn). This works in conjunction with GenericParameters.java which builds up the required data from the request. Apply this validation to every authenticated request to make sure that the data received is the data Blackboard Learn intended to send.
While the sample source code is the best description of the logic to be applied here, a text description can be found here.
| Note that while the rule is that 100% of the parameters (except mac) are included in the generation of the mac, there are a few that will be excluded. Refer to the sample code for the exclusion list. |
Events are deferred if they are generated at a point in time when the logically available proxy server is physically not available, perhaps shut down for maintenance. These deferred events will be sent the next time any action occurs which requires communication with the proxy tool and when the server is physically available.
There are a few events which may not be delivered at the same time as the event occurs. Currently this only applies to a few events. Each applicable event will be noted in the documentation for that event. The following situations result in the deferred delivery of an event:
- Restoring/importing a course which uses a proxy tool that is not installed.
- Restoring/importing a course which uses a proxy tool that is installed but not available.
- Restoring/importing a course which uses a proxy tool that is installed and available but not responding to requests.
- Copying a course which uses a proxy tool that is installed but not available.
- Copying a course which uses a proxy tool that is installed and available but not responding to requests.
- Copying a content item (without delete) whose content handler is a proxy tool that is installed but not available.
- Copying a content item (without delete) whose content handler is a proxy tool that is installed and available but not responding to requests.
- Copying a content item (with delete, to a different course) whose content handler is a proxy tool that is installed but not available.
- Copying a content item (with delete, to a different course) whose content handler is a proxy tool that is installed and available but not responding to requests.
The deferred events are sent to the proxy tool when it is installed (if required) and made available. As part of this action, any deferred events are sent to the proxy tool.
| There is a chance that you may receive deferred events multiple times; the proxy tool must be able to deal with this. The scenario that would produce this is as follows: T0: Your server is down T1: Events A, B, C are deferred T2: Your server is brought up again T3: The administrator saves the tool to activate sending the deferred events T4: After sending event A successfully, your server goes down again. At this point we will stop attempting to send the deferred events, but later on when we try to send them we will start with event A again. |
If the proxy tool uses tool settings then the deferred event will record the tool settings appropriate for the event at the time it was originally attempted to be sent. When it is eventually sent those original settings will be used regardless of the current settings at the time of the actual send.
It is possible to run multiple versions of a proxy tool server against the same Blackboard Learn instance. Since many components of the proxy tool are uniquely identified within Blackboard Learn this is not possible without some adjustments. To execute run-in-parallel mode, change these values to be unique within a different instance of your tool:
- program id (i.e. add a '-trial' to the end of the name)
- content handler handle (again, try adding -trial to the end of the handle)
Optional changes would be to alter these fields to make it easier to distinguish between the production and trial versions of your tool:
All authenticated requests to your proxy tool server will contain these arguments. Granted, in some cases these arguments do not apply, but you should look for them in all requests (but it is not an immediate failure if they are not found). You can refer to GenericParameters.java for a sample of how we're reading these parameters.
- ourguid - This is the GUID returned to you as part of tool registration.
- tcbaseurl - This is the basic scheme/host/port portion of the URL into the Tool Consumer for the current user session. Use this when generating the returnurl as well as when generating the css url.
- returnurl - This is an appropriate return-to-academic suite URL for the given operation. Once you have finished the workflow within your tool you should forward the user's browser to this URL. The value specified here will be relative to the tcbaseurl.
- locale - This is the current locale for the user's session. It is in java's string representation of a locale (lang_COUNTRY_variant). You should adjust the language in your tool's UI to reflect this locale.
- direction - This is the current html direction for the user's session. You should adjust the direction in your tool's UI to reflect this value : <html dir="(direction)"... - the value will be either ltr or rtl.
- timestamp - This is a timestamp field. It is used in the validation of the request.
- ticket - This is a ticket representing a webservice session for the currently logged in user. It can be used with the ContextWS.ticketLogin() method to get a valid webservice session. When you want to perform operations as the currently logged in user you must use this method. The ticket is only valid for a short time (currently 5 minutes) after it is generated so if you do not use it right away it may not be valid anymore. For example, if you plan on going through a series of pages with the user before you end up needing a webservice session for the user then you must do the ticketLogin first, before returning pages to the end user, or they might take 'too long' to fill in the pages resulting in the ticket not being valid anymore. Failure to follow this pattern could result in use cases "working in dev but not in production" because you might click through too quickly while testing your tool.
- userid - This is the userid of the current user. It is in an internal-id format specific to Academic Suite and is used in various calls into webservices to identify the user.
- role - This is the role of the current user within the current context. Possible values for this field are: COURSE:INSTRUCTOR, COURSE:TEACHING_ASSISTANT, COURSE:COURSE_BUILDER, COURSE:GRADER, COURSE:STUDENT, COURSE:GUEST, SYSTEM:ADMIN, and SYSTEM:NONE.
The COURSE: roles will be sent while the user is within a course context. The SYSTEM: roles are sent outside (i.e. on a portal tab not a course tab). - tcrole - This is the role of the current user within the current context. Currently this is identical to the role field for SYSTEM roles and is everything after the : for COURSE roles, but the intention is for the role field to be restricted to whatever we define as LTI roles in the LTI specification and this field is going to be defined as 'whatever we want' in the spec so this will reflect a larger range of potential roles eventually.
- mac - This is the generated MAC for the current request. Refer to authenticating requests for details on how to use this.
- ...settings... - If the tool has used the settings service methods (UtilWS.saveSetting()) then any settings it has stored
will be passed as generic arguments. Settings can be set at the system, course, or content item level. System settings will be sent to all operations on the proxy server. Course settings will be sent to all operations within a course. Content item settings will be sent for operations on a specific content item. Each setting will be passed using the key exactly as it was set in the saveSetting call. This means that it is the responsibility of the proxy tool developer to make sure they are not creating settings with names that conflict with any of the generic or action-specific arguments.
The following Use Cases are outlined here:
When a content item is created, the Proxy Tool provides an External ID during the creation. This is hidden inside the text body of the content item as a comment <!-ExternalId->. The Content.WS deals with encoding/decoding of this external ID so the remote system sees it separate from the description.
In all of these cases, the content items are copied normally; the framework takes care of generating all the new internal IDs but the content body remains the same. Any gradebook columns that are associated with the content items are copied as required with all Content-ID relationships maintained.
The remote system is responsible for creating new content instances on access by comparing the ID requested and the courseid/contentid/ASguid to the data it has recorded for that piece of content. If it receives a request for a piece of content with different ID information then it is responsible for performing the on-demand copying of the content item and updating it in Blackboard Learn. Refer to the sample code: ProxyUtil.fixContentAfterCopy(...) (and callers).
In addition, if a proxy tool has implemented a Tool Link type, this implements a single pass-through link to the remote system for the course.
Other issues involve point-in-time snapshots. While a proxy tool can try to perform on-access fix-ups (see ProxyUtil.fixContentAfterCopy) if they only focus on the definition (i.e. willing to always lose per-user data), they may not receive the correct version of the data in the copy.
| General Failure Note for the Following Use Cases: If a callback has been specified for any of these touch points and it fails then the operation should be rolled back and failed. |
Copy Content-Item Inside Course
The rules are:
- Copy content definition
- Copy associated per-user information
- Tool Data is not applicable for this use case
If the callback is not specified then this can be handled on-demand but the point-in-time state is lost. This only works if the user did not choose delete-after-copy OR the user did not implement the content-deleted callback in the proxy tool.
Copy Content-Item Between Courses
The rules are:
- Copy content definition
- Do not copy associated per-user information
- Tool Data is n/a for this use case
If the callback is not specified then this can be handled on-demand but we the point-in-time state is lost. This only works if the user did not choose delete-after-copy OR the user did not implement the content-deleted callback in the proxy tool.
Course Copy - Not an Exact Copy
The rules are:
- Copy content definition
- Do not copy per-user information
- Tool Data should be copied but without any per-user information
If the callback is not specified then the fixContentAfterCopy stage in the proxy server is only be allowed to copy the shell of content items. This means other use cases will fail, or at least not retain all the required information.
If the proxy tool is not available then the copy is performed as though there was no callback configured for the proxy tool. (Same for exact copy.)
The course-copied callback is performed as part of the settingcloneoperator (when the course_application table is copied which is the definition of these course-level tools).
Course Copy - Exact Copy
The rules are:
- Copy content definition
- Copy per-user information
- Tool Data should be copied with all per-user information
If the callback is not specified then the fixContentAfterCopy stage in the proxy server should only be allowed to copy the shell of content items. What this means is that THIS use case will not work (exact copy without a callback configured). The copy is not exact since it is missing all the per-user information.
The course-copied callback is called as part of the settings clone.
Export Course + Import
The rules are:
- Copy content definition
- Do not retain per-user information
- Maintain point-in-time state for the content definition
- Tool Data should be maintained without any per-user information
If the export/import callbacks are not specifed then the content/course can still be exported/imported but the point-in-time state is lost. The default fixContentAfterCopy logic will be allowed to copy the content description on demand which is the default rule for this use case.
If the proxy tool is not available then the export/archive/import/restore is performed as if there was no callback configured for the proxy tool.
| FAILURE Exception: For archive and export operations, if the content-exported callback is specified and fails then the operation still succeeds but the detailed log reflects the error that occurred. |
Archive Course + Restore
The rules are:
- Copy content definition
- Copy per-user information
- Maintain point-in-time state for the content definition
- Tool Data should be maintained with all per-user information
If the contentexported/imported callbacks are not specified for the content handler then this use case will not work. The fallback logic that must be implemented by the vendor is not allowed to copy per-user data. The default fixContentAfterCopy logic will be allowed to copy the content description "on demand" so we lose point-in-time but do at least get the raw content description which is a partial solution.
Archive Course + Import
The rules are:
- Copy content definition
- Do not copy per-user information
- Maintain point-in-time state for the content definition
- Tool Data should be maintained without any per-user information
If the export/import callbacks are not specified then the content/course can still be archived/imported but the point-in-time state will be lost. The default fixContentAfterCopy logic will be allowed to copy the content description "on demand" which is the default rule.
Archive course + Restore/Import on system connected to a different proxy server
In this case the rules are the same as Archive + Restore with one difference: the proxy server that handled the given content type is different on the new system.
Consider this scenario: Vendor A sells a proxy tool server that is installed on each customer's site. Customer X creates a course with content-handler A, backs it up, and restores it onto Customer Y's system that also uses content-handler A. But content-handler A is served using a different proxy server, one that is installed on Customer Y's site. During the restore, the proxy tool server will not have any knowledge of the declared backup and can therefore not link up the content.
The options for dealing with this situation are:
- FAD: You can only successfully restore/import with content+userdata/content if your target Blackboard Learn server is using the same proxy tool server as the source Blackboard Learn server. If it is different your content becomes unlinked, or becomes linked to new unpopulated content in the new proxy server.
- Add a proxy-tool-server-instance-id to the framework. This way a backup can identify itself by backupid and by proxy-tool-server-instance-id.
Exporting Proxy Tool Server Identification
When a course is archived/exported another resource is added to the file if any proxy tool callbacks are included:
<resource bb:file="resXXXXX.dat" bb:title="ProxyTool Instance Data" identifier="resXXXXX" type="course/x-bb-proxytoolinstance" xml:base="resXXXXX" />
The exported resource file will contain a small outline of each proxy tool instance that is defined on the current server:
<?xml version="1.0" encoding="UTF-8"?> <PROXYTOOLS> <PROXYTOOL> <BASEURL value="http base url of proxytool"/> <VERSION value="version of proxytool in use"/> <VENDOR value="vendorid for proxytool"/> <PROGRAM value="programid for proxytool"/> </PROXYTOOL> </PROXYTOOLS>
The sample proxy server includes Grade Center integration for content items. This proxy tool server requires some effort to add error checking, reviews, and other elements to prepare for a full-fledged Grade Center Web Service. This proxy tool server may have to be recreated for future Blackboard Learn releases.
While your Proxy Server will be responsible for rendering all of its own user interface pages, it is desirable for them to have the same 'look and feel' as the interface they navigated away from. To accomplish this there is a single css servlet which you can use in your pages to get an appropriate stylesheet for the user's current state. The path to this servlet is identified in the Tool Consumer Profile. You build up the full path using the tcbaseurl from the general arguments and then add on the following attributes:
- user_id (from general arguments)
- course_id (from general arguments if specified)
- ourguid (from general arguments)
- group_id (from Group Tool Action if specified)
- direction (optional: 'rtl' or 'ltr' if specified. If not specified will be based on the user's context. Only use this if you are not honouring the locale and direction passed to you in the original request).
Example Implementation: css.jspf and ProxyUtil.getCssPath
This example generates HTML on the page that looks like this:
<link rel="stylesheet" type="text/css" href="http://your.network.address:80/webapps/blackboard/execute/ proxyCSS?user_id=_1_1&course_id=_603_1">
The following is a sample XML description that would be provided to the registerTool method. Click the links for detailed information on the particular element.
<?xml version="1.0" encoding="ISO-8859-1"?>
<tool-profile ltiVersion="2.0-July08" xmlns:locale="http://www.ims.org/lti/localization">
<vendor>
<code>Vendor</code>
<name>Name Not Used</name>
<description>Description not used</description>
<url>http://www.your.url</url>
<contact><email>vendor contact not used.John.q.Admin@proxy.server.host</email></contact>
</vendor>
<tool-info>
<code>Program</code>
<name>Name Not Used</name>
<version>1</version>
<description>Text Description</description>
<tool-info>
<tool-instance>
<base-urls>
<base-url type="http">http://your.server.com/proxyApp</base-url>
<base-url type="https">https://your.server.com/proxyApp</base-url>
<base-url type="server-to-server">https://your.server.com/proxyApp</base-url>
</base-urls>
<contact><email>John.q.Admin@proxy.server.host</email></contact>
<security-profile>
<digest-algorithm>SHA</digest-algorithm>
</security-profile>
</tool-instance>
<required-webservices>
<tool-login>
<service name="Context.WS">
<operation>logout</operation>
... More <operation/> rows as required
</service>
... More <service ... elements as required for tool-authentication
</tool-login>
<ticket-login>
<service name="Context.WS">
<operation>logout</operation>
</service>
... More <service ... elements as required for ticket-authentication
</ticket-login>
</required-webservices>
<http-actions>
<action type="tool-provision" path="/tcProfileRegistration"/>
<action type="bundle" path="/getBundle"/>
<action type="remove" path="/removeAction"/>
<action type="config" path="/configAction"/>
<action type="state-change" path="/stateChangeAction"/>
<action type="reregister" path="/reregisterAction"/>
<action type="ping" path="/ping"/>
<action type="course-deleted" path="/courseHandler">
<param name="action" fixed="course-deleted"/>
</action>
<action type="course-copied" path="/courseHandler">
<param name="action" fixed="course-copied"/>
</action>
<action type="course-exported" path="/courseHandler">
<param name="action" fixed="course-exported"/>
</action>
<action type="course-imported" path="/courseHandler">
<param name="action" fixed="course-imported"/>
</action>
<action type="group-copied" path="/courseHandler">
<param name="action" fixed="group-copied"/>
</action>
<action type="group-exported" path="/courseHandler">
<param name="action" fixed="group-exported"/>
</action>
<action type="group-imported" path="/courseHandler">
<param name="action" fixed="group-imported"/>
</action>
</http-actions>
<links>
<content-handler>
<name locale.key="resource/x-my.content.type.name">Content Type Name</name>
<handle value="resource/x-my.content.type"/>
<http-actions>
<action type="create" path="/contentHandler">
<param name="action" fixed="create">
</action>
<action type="modify" path="/contentHandler">
<param name="action" fixed="modify">
</action>
<action type="remove" path="/contentHandler">
<param name="action" fixed="remove">
</action>
<action type="view" path="/contentHandler">
<param name="action" fixed="view">
</action>
<action type="viewattempt" path="/contentHandler">
<param name="action" fixed="viewattempt">
</action>
[<action type="content-copied" path="*/contentHandler*">]
<param name="action" fixed="content-copied">
</action>
<action type="content-exported" path="/contentHandler">
<param name="action" fixed="content-exported">
</action>
<action type="content-imported" path="/contentHandler">
<param name="action" fixed="content-imported">
</action>
[<action type="content-deleted" path="*/contentHandler*">]
<param name="action" fixed="content-deleted">
</action>
</http-actions>
<can-copy value="true"/>
<icons>
<icon>unused default lti icon</icon
<icon platform="blackboard" style="toolbar" locale:key="icon.lang.key">/images/icon1_on.gif</icon>
<icon platform="blackboard" style="listitem" locale:key="icon.lang.key">/images/icon1_on.gif</icon>
</icons>
</content-handler>
... More <content-handler>...</content-handler> sections
<menu-link>
<category-choice>
<category>TBD - LTI defined generic category currently unused</category>
<category platform="blackboard">course_tool</category>
</category-choice>
<name locale:key="course_tool.name.key">Link Name</name>
<http-actions>
<action type="menu-view" path="/courseTool"/>
</http-actions>
<description locale:key="course_tool.link.description.key">Link Desription</description>
<icons>
<icon>unused default lti icon</icon
<icon platform="blackboard" style="listitem" locale:key="icon.lang.key">/images/icon1.gif</icon>
</icons>
</menu-link>
... More <menu-link>...</menu-link> sections
</links>
</tool-profile>
Follow these steps to prepare a proxy tool server development environment:
- Login as administrator to a current AS install.
- Go to System Admin/Building Blocks/Proxy Tools.
- Select Download Sample Tools.
- Unzip into a directory on your hard drive.
- Read LICENSE_for_samples.txt.
- Read readme.txt.
- Read proxy/java/readme.txt.
SSL support is centered on Tomcat; Blackboard directs users to refer to the following web site for information regarding SSL support:
Several of the touch-points between Blackboard Learn and the proxy tool server are through server-to-server posts. These are authenticated requests and require the normal MAC validation, but the response is a simple XML response instead of a resulting HTML page. There are only two possible responses to any simple XML request.
Success:
<result>SUCCESS<result>
Failure:
<result>ERROR<result>
When implementing the proxy server, these requests respond should within a reasonable time period (sub-second if possible). There is a timeout of 2 minutes applied to the socket so if the request takes longer, the callback is assumed to have failed. If more time is needed then edit the timeout setting in bb-config.properties as follows:
bconfig.proxy.server.socket.timeout=timeout in ms
This must be set to at least a value of 1000 (1 second) and applies to all proxy tools.
As part of LTI compatability, a Tool Consumer Profile is generated which describes Blackboard Learn from the perspective of tool interoperability. This is a fixed URL on any given Blackboard Learn instance:
webapps/ws/wsadmin/tcprofile
The System Administrator can disable the generation of this profile on the Proxy Tool Global Properties page. This returns an XML description that includes the following items:
- http url of this system
- https url of this system
- css url
- webservices offered (including url of webservice reflecting ssl choice), operations offered per Web Service
Here is the Tool Consumer Profile:
<?xml version="1.0" encoding="UTF-8" ?>
<tool-consumer-profile ltiVersion="2.0-July08">
<tool-consumer-info>
<name>Blackboard Learn</name>
<version>9.0.236.0</version>
<description>The Blackboard Learning System(TM) is a family of software applications designed to enhance teaching and learning. Intuitive and easy-to-use for instructors, the Blackboard Learning System helps instructors to build course materials online and engage with students in an interactive way.</description>
<security-profile>
<digest-algorithms>
<algorithm>MD5</algorithm>
<algorithm>SHA-1</algorithm>
</digest-algorithms>
</security-profile>
</tool-consumer-info>
<tool-consumer-instance>
<guid>6b9de60425e243fba3bfbd283158864d</guid>
<contact>
<email>TODO - what email do we want to expose here?</email>
</contact>
</tool-consumer-instance>
<vendor>
<code>blackboard</code>
<name>Blackboard Inc.</name>
<description>Blackboard Inc. (Nasdaq: BBBB) is a global leader in enterprise technology and innovative solutions that improve the experience of millions of students and learners around the world every day. Blackboard's solutions allow thousands of higher education, K-12, professional, corporate, and government organizations to extend teaching and learning online, facilitate campus commerce and security, and communicate more effectively with their communities. Founded in 1997, Blackboard is headquartered in Washington, D.C., with offices in North America, Europe, Asia and Australia.</description>
<url>http://www.blackboard.com</url>
<contact>
<email>sales@blackboard.com</email>
</contact>
</vendor>
<http-actions>
<action type="css" path="/webapps/blackboard/execute/proxyCSS" />
</http-actions>
<services-offered>
<service name="Content.WS" url="http://lance.bbbb.net/webapps/ws/services/Content.WS" wsdl="http://lance.bbbb.net/webapps/ws/services/Content.WS?wsdl">
</service>
... More <service></service> blocks
</services-offered>