CC SAIL Micro Portal

Sail Portal REST Protocol for Learner Data Persistence

This document describes the first implementation. This implementation combines a simple SAIL portal, with a reporting capability for introspecting learner interaction with an instrumented experimental-type model interaction along with an initial implementation of Sail Data Services (SDS). The SDS is being separated from this portal so any portal can use the SDS.


A user will login to the portal and will be displayed a list of available projects they can run. The projects listed are pulled from active offerings they are eligible for. The first portal implementation will stub this out and will effectively make every project available to everybody. The portal is located at:

Select Offerings to see the available projects. Running a project means the portal will deliver a jnlp to the user that will allow Sail and Pas to start. An argument passed in the jnlp will specify a url that returns the userOfferingConfig data in xml format. The argument element in the jnlp will look like this:

<application-desc main-class="net.sf.sail.emf.launch.EMFLauncher2">
	<argument>
		http://rails.dev.concord.org/sail/offering/config/2/2
	</argument>
</application-desc>

and and on the portal those numbers mean this:

http://dev.rails.concord.org/sail/offering/config/:offering_id/:user_id

This url will return the userOfferingConfig for offering #2, and user #2 in xml format and look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder" version="1.4.0">
  <object class="net.sf.sail.core.service.impl.CurnitUrlProviderImpl">
    <void property="url">http://tels-develop.soe.berkeley.edu:8080/maven-jnlp/curnit-airbag.jar</void>
  </object>
  <object class="net.sf.sail.emf.launch.PortfolioManagerService">
    <void property="portfolioUrlProvider">
      <object class="net.sf.sail.core.service.impl.UrlStringProviderImpl">
        <void property="urlString">
          <string>http://rails.dev.concord.org/sail/offering/bundle/2/2</string>
        </void>
      </object>
    </void>
    <void property="bundlePoster">
      <object class="net.sf.sail.emf.launch.BundlePoster">
        <void property="postUrl">
          <string>http://rails.dev.concord.org/sail/offering/bundle/2/2</string>
        </void>
      </object>
    </void>
  </object>
  <object class="net.sf.sail.core.service.impl.LauncherServiceImpl"/>
  <object class="net.sf.sail.emf.launch.EMFSailDataStoreService"/>
  <object class="net.sf.sail.core.service.impl.UserServiceImpl">
    <void property="participants">
      <void method="add">
        <object class="net.sf.sail.core.entity.User">
          <object class="net.sf.sail.core.uuid.UserUuid">
            <string>70e89b1a-1955-11db-ada9-0016cb92217d</string>
          </object>
          <string>Stephen Bannasch</string>
        </object>
      </void>
    </void>
    <void property="userLookupService">
      <object class="net.sf.sail.core.service.impl.UserLookupServiceImpl"/>
    </void>
  </object>
  <object>net.sf.sail.core.service.impl.SessionLoadMonitor</object>
  <object>net.sf.sail.core.service.impl.SessionManagerImpl</object>
</java>

With this config information the program can use the url specified in the portfolioUrlProvider element to get a collection of bundleSessions as a sailuserdata:ESession. A userOfferingBundleCollection with two empty sessionBundles would look like this:

<?xml version="1.0" encoding="UTF-8" ?>
  <sailuserdata:EPortfolio xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:sailuserdata="sailuserdata">
    <sessionBundles/>
    <sessionBundles/>?
  </sessionBundleCollection>

Each sessionBundles holds the learner data added by a single session. A session is defined as the learner data created between the time a curnit is loaded to the time a curnit is unloaded (usually when the client is exited). If there are no sessionBundles the sailuserdata:ESession element will be empty.

When a sessionBundles is created by the client it will be saved to the bundlePoster with an HTTP POST. This is demonstrated with curl below. All data sent to the portal from the client (HTTP POST and PUT) interactions from the client to the portal should set the HTTP Content-Type: header to 'application/xml'. Any HTTP GET request sent by the client to the portal should set the HTTP Accept: header to: 'application/xml, /; q=0.2'. Since no data are sent or received with an HTTP DELETE request not special header is needed.

curl -i -X POST -H 'Content-Type: application/xml' -d "<sessionBundles/>" http://dev.rails.concord.org/sail/offering/bundle/2/2

If the post was successful the following HTTP response codes (among others) will be returned:

HTTP/1.1 201 Created 
  Location: http://dev.rails.concord.org/sail/offering/bundle/2/2/20

The Location response returns a URI for the specific sessionBundles. Normally this resource will not be accessed directly since the client will instead be asking for the complete sailuserdata:ESession.

It is the responsibility of the client program to make sure the content of the sessionBundles are well formed XML.

To retrieve the sailuserdata:ESession do a HTTP GET to the portfolioUrlProvider. Make sure and set the Content-Type: header to 'application/xml'. Here is an example using curl.

curl -i -X GET -H 'Accept: application/xml, */*; q=0.2'  http://dev.rails.concord.org/sail/offering/1/3

If the collection is returned successfully the following HTTP response codes (among others) will be returned:

HTTP/1.1 200 OK 
  Content-Type: text/xml; charset=UTF-8
  Content-Length: 157

And this is what the Content will look like after the third sessionBundles was POSTed to the userOfferingBundleUrl (there will probably not be pretty indenting)

<?xml version="1.0" encoding="UTF-8" ?>
  <sailuserdata:EPortfolio xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:sailuserdata="sailuserdata">
    <sessionBundles/>
    <sessionBundles/>?
    <sessionBundles/>?
  </sessionBundleCollection>

A sessionBundle with data in it might look like this:

<sessionBundles xmlns:xmi="http://www.omg.org/XMI" xmlns:sailuserdata="sailuserdata">
<sockParts podId="dddddddd-0002-0002-0000-000000000000" rimName="modelActivityData">
<sockEntries value="<modelactivitydata> ... lots more encoded XML deleted"/>
</sockParts>
</sessionBundles>

For debugging purposes the following additional HTTP verbs are supported:

  • PUT: this will replace an existing sessionBundles.
  • DELETE: this will remove an existing sessionBundles.

However to use these verbs you will need the database id of the bundle record. Currently this is only supplied in the Location header returned when POSTing a bundle.

Here is an example using curl to do a PUT that replaces the first sessionBundles:

curl -i -X PUT -H 'Content-Type: application/xml' -d "<sessionBundles>some data</sessionBundles>" http://dev.rails.concord.org/sail/offering/bundle/2/2/18

If successful the following HTTP header will be returned:

HTTP/1.1 204 No Content

Here is an example using curl to do a DELETE that removes the last sessionBundles:

curl -i -X DELETE http://dev.rails.concord.org/sail/offering/bundle/2/2/20

If successful the following HTTP header will be returned:

HTTP/1.1 204 No Content

For more details about the HTTP operation include the option " --trace - " in the curl command and the complete transaction will be echoed to the shell.

This first version of the Sail Portal REST protocol does not have any authentication or authorization features except that to get the jnlp in the first place a user needs to be registered and logged in.

When you POST a userOfferingBundleCollection for now I will be expecting a single sessionBundles but there is no reason not to allow you to post multiple sessionBundles which presumably happened because you were offline for multiple sessions.

In the future I'll supply more user personal, authentication, and encrypting key info in userOfferingConfig which would allow the client to authenticate a user while offline and also allow client to encrypt locally cached info and to create more trust with the portal when sending userdata back.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.