Monday 29 June 2015

Free books from IBM

It will be a very short post, it's my holiday afterall but I will be back shortly ;)

IBM Redbooks are available for Google Play or iTunes Store. Most of them, if not all, are free!
Have a look at Cloud Computing Patterns of Expertise for many useful tips on cloud application development. Ideal for a quick summer read ;)

Monday 22 June 2015

OSLC Resource Selection

Last time I presented idea of OSLC DXL Services which can very powerful and useful.
One can write a DXL services against database, projects or modules, but providing DOORS resources links manually might be very inconvenient.
There is much easier way to select your resource.
DWA server is OSLC RM Provider and as such it provides resource selection delegated UI.
You can find more information on Delegated UI here.
So before I will continue with next more complex examples of OSLC DXL Services or more powerful Node.JS applications I will focus on simple resources selection.
In terms of OSLC you can select Requirement Collection (a Module) or single Requirement (Object/Requirement).

Protocols

There are two protocols which OSLC Consumer could use to select resouce:
  • #oslc-core-postMessage-1.0 - Indicates that the Post Message protocol is to be used
  • #oslc-core-windowName-1.0 - Indicates that the Window Name protocol is to be used
Here I will show how to use post message protocol which works with all modern browsers. OSLC specification provides UI consumer's responsibilities which should be followed:
  1. Include the Delegated UI Dialog via iframe (i.e setting iframe src to the URI of the Delegated UI Dialog) or via an embedded browser. Append the fragment identifier #oslc-core-postMessage-1.0 to the URL to indicate that Post Message is the desired protocol.
  2. Add a 'message' listener to the outer window to receive messages from the Delegated UI Dialog.
  3. Listen for window 'message' events, ignoring events from other sources or not prefixed with "oslc-response:"
  4. When message from Delegated UI Dialog indicates user completed action, free resources and handle action. 

1. Providing an iframe for Delegated UI

Let's look at very basic HTML page:
<!DOCTYPE html>
<html>
<head>
    <title>DWA OSLC Delegated UI test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="/scripts/delegatedui.js"></script>
</head>

<body >
    <h1>DWA OSLC Delegated UI test</h1>
    <div>
        <button id='buttonSelect' type="button" onclick="delegatedUI.selectResource()">Select module</button>
    </div>
    <div id="OSLCMainDiv"></div>
    <div id="anotherDiv"  >
    </div>
</body>
Here I have just a button and some divs. You can find full source of /scripts/delegatedui.js here.
Button "Select Module" has following callback:

function selectResource() {
        clear();
        
        // Add 'iframe' element dynamically
        var frameName = "";
        frame = createElement("iframe", frameName);
        frame.id = "OSLCContainer";
        frame.width='400px';
        frame.height='500px';

        addEvent(window, "message", onMessageReceived);
        
        var mainDiv = document.getElementById("OSLCMainDiv");
        if (mainDiv) {
            mainDiv.appendChild(frame);
        }

        frame.src = "https://server:port/dwa/rm/calm/v2/selectRequirementCollection" + "#oslc-core-postMessage-1.0";
    }

This simply dynamically adds an iframe, registers its callback and appends "#oslc-core-postMessage-1.0" as chosen protocol.
Of course one should discover OSLC Service Provider selection dialog using Service oslc:selectionDialog property (as defined specification), but I used general DWA schema for requirement collection (a module) selection.

2. Adding message listener

We need to registering 'message' event listener as called from selectResource function:

function addEvent(obj, type, func) {
        if (obj.addEventListener) {
            obj.addEventListener(type, func, false);
        }
        else if (obj.attachEvent) {
            obj.attachEvent("on" + type, func);
        }
        else {
            obj["on" + type] = func;
        }
    } 

3. Listen to events

Once message is registered we have to listen to incoming events ignoring those not prefixed with "oslc-response:" :

 function onMessageReceived(event) {
        if (frame && event.source === frame.contentWindow) {
            var prefix = "oslc-response:";
            if (event.data.substring(0, prefix.length) === prefix) {
                done(event.data.substring(prefix.length));
            }
        }
    }


With 'done' method triggered when 'oslc-response' is found in event data:
function done(data) {
        var title = JSON.parse(data, function(key, value) {
            if (key.equals('rdf:resource')) {
                clear();
                // TODO: add you logic here
                // value contains a link to the resource
                // title is a Module name
            }
        });
    }

4.  Cleanup


Whatever cleanup you require that's the place to do it. I simply remove listener and iframe element

function clear() {
        var mainDiv = document.getElementById("OSLCMainDiv");
        if (mainDiv) {
            // clear any existing child elements
            mainDiv.innerHTML = ''; 
        }

        removeEvent(window, "message", onMessageReceived);
    }

Friday 5 June 2015

OSLC DXL Services

One of the primary strategies of the IBM Rational division is to continually improve upon the integration capabilities of Rational and non-Rational products.
Keystone to this strategy is Open Services for Lifecycle Collaboration (OSLC). 
However a common complaint heard from providers / consumers of OSLC functionality is that there can be certain business problems that cannot be addressed because they rely on data and/or functionality that are (a) not currently available in the implemented standard or (b) considered too specialized for inclusion in the standard and therefore not planned for future versions.
Yet all is not lost and as OSLC service discovery is extensible it is possible to add services to help close such gaps. 
An excellent example of this can be seen in the introduction of OSLC DXL Services to Rational DOORS.

What are OSLC DXL Services?

Put simply they are an extension to the Rational DOORS OSLC RM V2 interface which allows users to execute Rational DOORS DXL scripts safely and securely across HTTPS.

Yes you can run DXL scripts from DWA! Well not all DXL scripts you have of course and only those registered by Administrator as OSLC DXL service but still this gives your project new possibilities and powers.

What makes DXL Script a OSLC DXL Service?

If you looked at IBM OSLC DXL Service introduction you came across helloWorld.inc:

void getHelloString(string language)
{
    string hello = null
    if ("French" == language) {
        hello = "Bonjour le monde" 
    }
    else if ("Finnish" == language) {
        hello = "Hei maailma"
    }
    else if ("Latin" == language) {
        hello = "Ave mundi" 
    }
    else {
        hello = "Hello world" 
    }

    setDxlServiceResult hello
    print hello "\n" 
}
If you are familiar with DXL you can spot setDxlServiceResult PERM. That is exactly what will set value of hello as the result of the service.

Registering DXL Service

Currently DOORS Database Administrator has to add those services manually using DOORS Perms.

OSLCDXLService os = null
string err = null
string dxlCode = "#include <addins/services/helloWorld.inc>"
err = addOrUpdateOSLCDXLService("helloWorld", "Hello world in several languages", dxlCode, "getHelloString")
if (!null err){
      print err
} else {
      print "Installed Service\n"
}

Quick PERM reference (as it's not yet documented in DOORS DXL Reference Manual)

void setDxlServiceResult(string s)
sets s as the result for DXL Service

string addOrUpdateOSLCDXLService(string n, desc, dxlcode, entryPoint) 
registers dxlcode as the service with name n and description desc. entryPoint specifies the function in dxlcode to be run as entry point of service.

To make life easier OSLC DXL Services management will be soon available in File menu in IBM DOORS client.


Call OSLC DXL Service

Once you have your service registered you can send GET and PUT requests.
Set both the accept and content-type headers to this entry: application/rdf+xml and submit:
 
  • GET request for the DXL service description.
  • PUT request with optional parameters to invoke the service.

helloWorld service registered above could be accessed with URI like https://servername:portnumber/dwa/rm/dxl/helloWorld

As you notice before helloWorld function takes one parameter which should be supplied. You can provide parameters for DXL Services with aid of request content:


<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">
  <doors:Arguments>
  <doors:arguments>French</doors:arguments>
  </doors:Arguments>
</rdf:RDF>

If you have more then one argument, separate those with comas ','. All arguments are passed to DXL service as strings. If you need to have other types use DXL conversions in your service.


The results


Once DXL Service will finish its operation DWA server will return response with a content in following form:

  dwaOauth.js
was used to GET only protected information from DWA. But calling OSLC DXL Services requires PUT request which should be added.

There's nothing easier:

exports.getDXLServiceResult = function(req, res, dxlService, lang, next, onError) {
    var oa = new OAuth(req.session.oa._requestUrl,
              req.session.oa._accessUrl,
              req.session.oa._consumerKey,
              req.session.oa._consumerSecret,
              req.session.oa._version,
              req.session.oa._authorize_callback,
              req.session.oa._signatureMethod);

        // Try to get protected data
        var post_body = '<?xml version="1.0" encoding="UTF-8"?><rdf:RDF    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"    xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">'
        post_body += '<doors:Arguments><doors:arguments>' + lang +'</doors:arguments></doors:Arguments></rdf:RDF>';

        oa._headers['accept'] = "application/rdf+xml";

        var host = url.parse(location);
        console.log('requesting service ' +host.protocol+"://"+host.host+'/dwa/rm/dxl/'+dxlService + ' with param: '+lang);

        oa.put(host.protocol+"//"+host.host+'/dwa/rm/dxl/'+dxlService,
            req.session.oauth_access_token,
            req.session.oauth_access_token_secret,
            post_body, 
            'application/rdf+xml',
            function (error, data, response) {
                if (!error && next) {
                    next(req, res, data);
                }
                else if (onError) {
                    onError(error);
                }
            }
        );
}

And that's it!.