Implementing SCORM: Building the API

September 23rd, 2003 § 5 comments

Continuing my ongoing series about SCORM, this entry discusses some specific aspects of the implementation of the SCORM API itself, considering both its client and server sides, and also dealing with the resolution of some of the technical difficulties found.

As I mentioned in the previous entries, the standard is still immature in some aspects. The API itself, although it presents as well defined interface today, has some inherent flaws that can make a rigorous implementation of all of its functionality a complicated work.

With regards to those flaws, the biggest problems they presented to me were: the JavaScript security model, the creation of a synchronous cross-platform JavaScript HTTP client, and the management of the API sessions. The first problems comes from the fact the a given JavaScript code can call additional code only from URLs in the same domain from which it was first invoked. To be fair, this is not a JavaScript problem. This limitation exists to increase the security of code running in Web pages; if it didn’t exist, we would see much more JavaScript-based exploits than we see today. Moreover, the JavaScript security model follows closely the other sandboxed security models implemented by the browser, whether they refer to Java applets or communication among frames in a give page.

Even so, this limitation can become a stumbling block in an implementation because, in some cases, it will prevent some uses of the API. If that happens, there is no ready solution but requing the API to be invoked from the same domain from which the course is being run. Any other solutions will imply in a reduction of the security level the client is running in, or additional configurations for each user, none of which are interesting if the system needs to be user-friendly and safe.

The second problem is how to access arbitrary URLs from a JavaScript function in a synchronous way. As many implementations of the SCORM API will reside in a server that will be accessed by the course, it’s necessary to devise a way to transfer between the course and the server. A common implementation of the API will present the API interface to the course, but will delegate the actual execution of the functions to a server-side API. The API request then becomes a two-stage call: the course invokes the API proxy provided to him, and the API proxy invokes the actual API in the server via some RPC mechanism, getting and returning the necessary information.

The division is necessary because the data must be persisted in some place, and because some API calls will need to verify multiple sources of information to return or save some value. It’s not possible to use the usual form submission mechanism in a SCORM implementation because a given course needs to be as generic as possible, only using resources common to all platforms the course will run on (namely, browsers). So the API must be broken in two parts: a local wrapper that will handle the interface with the course; and a Web service (the term is used in its loosest sense) that will receive requests from the wrapper, will handle them appropriately, and will return the necessary information back to it so that it can, in turn, return them to the course.

Unfortunately, there isn’t a single, common mechanism that can be used as a transport in all browsers in use today. Even Java applets, which would have been considered the most interesting solution a few years ago, now requires a 5MB plugin to run in two of the most used browsers today: Internet Explorer 6 and any Gecko-based browser.

To solve this problem, I decided to create a fallback scheme in the local API wrapper that will find the best transport mechanism available for a browser and use it, trying subsequent alternative mechanism depending on the conditions under which the course is being run. The course will invoke the wrapper, which implements the API, and the wrapper will transparently find the best transport mechanism for the browser in use.

The transport mechanisms I used are: XML in Internet Explorer 5+; XML in Mozilla; Java in both when the use of XML is not possible; ActiveX in Internet Explorer if the use of Java or XML is not possible; and Java in the other browsers. The implementation first tries the faster and easiest transport mechanism. If the mechanism can’t be used because of some specific reason, it will create the conditions to use the next mechanism, trying to use it then. That procedure is repeat until a mechanism can be used. If no mechanism can be used, the usual error processing takes place. To the course, this is transparent, as aforementioned.

Contrary to what I first thought, that solution worked beautifully, and its implementation took no more than a few hundred lines of JavaScript, most of which handle the detection of the support for the transport mechanisms. The requisition code itself took less than a hundred lines.

On the server side, the API was implemented as usual, using a dynamic page, which was implemented in a language supported by the server. The API uses a previously agreed upon format to communicate with the local wrapper. For most transport mechanisms, the format will be XML, but it can fallback to CSV if the transport doesn’t support XML, as it the case with Java, which doesn’t support XML out-of-the-box in most virtual machines available with the browsers in use today.

The third problem had a simple solution. Although the SCORM specification doesn’t make it clear, each course establishes a session for each user. This sessions needs to finish cleanly or errors will be returned in the next calls to the API. However, because of the nature of the technologies used to implement it, in some cases the API may fail to finish cleanly. For example, the browser may crash or fail to invoke the unload code of a page for any reason.

To avoid any problems with unfinished sessions, I decide to pass a random session number to the API itself. The number is generated by the local wrapper, which sends it to its server-side counterpart. The course itself doesn’t know about it. The session number is used to uniquely identify a user session in a given course, and provides a mean to detect if a given session finished uncleanly. If the number received is different from the previous invocation for the same course and user, the server-side part of the API assumes the session failed to finish, and restarts its processing.

Apart from those three problems, there were other small problems resulting from the interpretation of the API specification itself. However, as those problems are more related to the interpretation of the specification itself, they caused no technological problems, and I will not talk about them here.

A important detail to remember when implementing the API is that the local wrapper is provided by the LMS, even though it executes on the client. So it can make use of information that only the LMS has access to when it is initialized. If fact, that is how it negotiates the user session with its server-side counterpart. The only obligation the local wrapper has towards a course is to provide the necessary SCORM interface.

The implementation of the server-side API is not difficult, but requires a special care in the synchronization of the session data. The server-side API is responsible for keeping the session state consistent. For example, when a course module informs its “cmi.core.score.raw” value, the API must save the value, and, if the module is for credit, change the value of the “cmi.core.lessonstatus” element to reflect the situation of user regarding the module. Another example comes from the “cmi.core.sessiontime” and “cmi.core.total_time”, which work closely with each other. The API is responsible for keeping the synchronized and consistent.

I found that the easiest way to implement the API in the server was to use a dictionary table in the LMS database describing how the implementation will handle specific elements. The table specifies whether the elements are read-only or write-only, whether they are persistent or handled only at run-time, and so on. That makes implementing each individual element a simpler task.

Also, I created some extension elements to help with the implementation. For example, I create an “ext.api.sessionid” element to keep track of the session number aforementioned. No course will see this element, as it’s only used internally. Another elements I created were “ext.api.state”, “ext.api.lasterror”, and “ext.api.last_diagnostic” to help in the implementation of the core methods of the API. Obviously, the “ext” prefix is a bad choice, and I used another internal prefix that won’t likely conflict with future SCORM elements.

As the local API wrapper, the server-side part of the API took no more than a few hundred lines of ASP, which is not a modularized language. A more sophisticated language would probably required less lines of code. This number refers to the mandatory elements of the API.

In short, implementing the base SCORM API is a arduous task, but not a Herculean task at all. The implementor must take care to provide both forward and backward compatibility to avoid problems with the courses that will be used in the LMS, but the most complicated part of the implementation is the task of interpreting the specification itself. Some of the information there is a bit ambiguous, and the programmer must be careful when deciding what way to follow, as is the case with almost all standards related to technology.

§ 5 Responses to Implementing SCORM: Building the API"

  • Giovanni Santini says:

    Hi, I try to do something like you. I post the SCORM data without exchange data with XML. But javascript is synchronous! So I can do one operation. If thw WBT call for multiple tracking I can handle the situation. Have you an idea how can I resolve this problem?
    Thank you.

  • Ronaldo says:

    About your questions, JavaScript can be asynchronous if it’s needed, but there’s no reason to do so with SCORM. In all the time I’ve been working with the standard, there has been no occasion where it was necessary to post simultaneous requests. You can just do one request after another and collect the results.

  • keltoi says:

    Is it possible to implement the API client piece with Javascript alone? (No applets nor other elements apart from JavaScript and HTML).

    I’m starting to lose my faith ;( and don’t wanna use applets (and, of course, not even thought of ActiveX).

    Please, show me the light! :)


  • Ronaldo says:

    As I said in the post, it’s possible to implement the API with JavaScript alone as long as you use XML, with means only Internet Explorer and Mozilla, with different transport implementations too.

  • Samuel Genus says:

    I am trying to create an API that will receive the request from the SCO and interact with the LMS. I’m not really sure where to begin and what code language to use. Any suggestions.

What's this?

You are currently reading Implementing SCORM: Building the API at Reflective Surface.