JAX-RS REST with Swagger in IBM Integration Designer

We have seen over the last 2-3 years an evolution in the way how we are interacting with our services.  This is thanks to mobile devices which don’t have the same processing power like our servers in the data centers and also bandwidth data restrictions might play here an important factor. Enterprise Services in a Service Oriented Architecture use(d) HTTP/SOAP as an interoperability protocol. A lot of software products have Webserviece APIs via SOAP but due to the growing business demand to go mobile these interfaces don’t play very well with our devices. Besides the already mentioned factors the computation cost of SOAP message data serialization is notable, that’s why HTTP/JSON has taken over the Mobile and Cloud world. Though JSON has many obvious advantages as a data interchange format – it is lightweight, human readable, well understood, and typically performs well – it also has its issues (I will save this for another article).

To everyone working with Web services the standard definition file is a WSDL file which basically is a services contract between client and server with well defined objects and schemas, containing either document-oriented or procedure-oriented information. In WebSphere Integration Developer (WID) or now IBM Integration Designer (IID) you can easily create a Web service HTTP/SOAP Binding with a WSDL file or use a HTTP Binding with a builtin JSONDataHander to expose your service as a RESTful service in a couple of clicks. Sadly at this point no one knows how to use our RESTful API and you have to spend time to write additional documentation or define a WADL file, which is incredibly time consuming to create descriptions with.

Fortunately there is swagger.io a framework to help fill the gap. Swagger is a specification for documenting REST APIs. It specifies the format (method, url and representation) to describe REST Web services. It provides also tools to generate the documentation from application code.

IBM BPM 8.5.x  is using WAS 8.5.x as runtime platform, which as part of the official EE6 platform supports JAX-RS. Integration Designer doesn’t support neither JAX-RS nor Swagger as part of the auto-generated Bindings. There is a RFE for this feature. Luckily it supports Web Application development and that’s what I’m going to use to create a JAX-RS Web Interface, declare swagger annotations and call out via a SCA Reference Partner to my Mediation.

We are going to start with the interface, objects and the Stand-alone Reference component. The Name of the standalone ReferencePartner is “CarPartner”.

jaxrs-1

 

jaxrs-1a

Now let’s create a Web Application Module (war)

jaxrs-1b

and link it to our Mediation Module

jaxrs-1c

When cleaning/building your workspace, IID compiles your application so any modifications done in the default WEB module will be erased, that’s another reason for using a separate custom Web Module. From here on, you can start creating your JAX-RS Interface and write the necessary code to initialize swagger and also make the connection to your SCA Component.

In order to give swagger the knowledge of the structure of the schema objects in use for the JAX-RS interface we have to generate JBAX-POJOs/SDO implementation classes because swagger is not designed to understand XSDs. You can do that by switching from default (Business Integration) perspective to Java perspective and use the builtin wizard to generate Java classes.

jaxrs-1d

If you are using Maven or Gradle you can get the swagger libraries from the Maven repo. For the purpose of this walk through I have extracted the necessary jar files from the POM.XML via

mvn install dependency:copy-dependencies

and copied them into the WAR/lib folder. There are also two additional libraries for the CORS filter support which are needed to grant access to swagger-ui.

jaxrs-1e

Let’s assemble everything. There are various ways to initialize swagger. First update the web.xml with the following entries:

<servlet>
<description>
This is the description for the sample servlet
</description>
<display-name>Gateway</display-name>
<servlet-name>com.mygateway.GatewayApplication</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>com.mygateway.GatewayApplication</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

Create an Application class with the following entries: (more info can be obtained in the swagger documentation)

package com.mygateway;

import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;

public class GatewayApplication extends Application {

 public GatewayApplication() {
 BeanConfig beanConfig = new BeanConfig();
 beanConfig.setTitle("PoT SWAGGER - REST GATEWAY");
 beanConfig.setDescription("Test API REST Interface with Swagger Annotations");
 beanConfig.setVersion("0.0.1");
 beanConfig.setSchemes(new String[] { "http" });
 beanConfig.setHost("localhost:9080");
 beanConfig.setBasePath("/Gateway/rest");
 beanConfig.setResourcePackage("com.mygateway");
 beanConfig.setScan(true);
 beanConfig.setPrettyPrint(true);
 }

 public Set<class<?>> getClasses() {
 Set<class<?>> classes = new HashSet();

 //JAX-RS interface
 classes.add(GatewayService.class);
 //Add Swagger initialisation classes
 classes.add(ApiListingResource.class);
 classes.add(SwaggerSerializers.class);

 return classes;
 }
}

My JAX-RS class (GatewayService.java) has the same 3 operations as my SCA Interface. Once the HTTP call is initialized a call-out is made via the SCA Partner “CarPartner” to my Mediation and the response is feed though a SDO/JSON data formatter.

@Api(value = "/cars", description = "RESTful API to interact with Car Servcie.")
@Path("/cars")
@Stateless
public class GatewayService extends Application {

 @ApiOperation(value = "Get Car by ID", notes = "Get model details by ID", responseContainer = "Car", response = Car.class)
 @GET
 @Path("/{id}")
 @Produces(MediaType.APPLICATION_JSON)
 public Response getById(
 @ApiParam(value = "Car ID", required = true) @PathParam("id") int id)
 throws Exception {

 // Invoke SCA Stand-alone Reference Partner
 Service service = (Service) ServiceManager.INSTANCE.locateService("CarPartner");
 DataObject car = (DataObject) service.invoke("getCarById",Integer.valueOf(id));
 DataObject res = (DataObject) car.get(0);

 //Use SDO to JSON Converter
 com.ibm.bpe.message.JsonConverter jc = com.ibm.bpe.message.JsonConverter.getInstance();
 String resp = jc.convertObject2JSON(res);

 if (res != null) {
 return Response.ok(resp).build();
 }
 return Response.status(Response.Status.NOT_FOUND).build();
 }
...
...
}

The response class definition points to the SDO Java implementation generated though the wizard.

 response = Car.class

You might ask why I’m using the generated Java classes only for swagger presentation purpose? Well, many ways lead to Rome. You could use for the Response following approach:  EMF EObjects or SDO EDataObjects to Java POJO to JSON, or create a dynamic EMF model from XSDs, or serializae JSON to XML to SDO. I still believe a simple SDO/JSON transformation will give the best performance. On the other hand swagger needs a strong typed definition of your objects, whereby the DataObject implementation is dynamic, hence we use the generated classes for the swagger presentation layer.

After the deployment of the APP to Process Server we can invoke the REST service.

jaxrs-1g

Now it’s time to see the service definition in my local swagger-ui.

swagger2

Swagger-ui with Websphere Application Server

To deploy the swagger-ui on WebSphere Application Server get the source code from github first. Create a Simple Web Project in Eclipse and copy the content of the ‘dist’ folder from the zip archive into WebContent folder. Make sure you have the WebSphere Development Tools plugin installed in Eclipse. You can find it in the Eclipse Marketplace.

Now set the root context to /swagger in ibm-web-ext.xml. Export the project as WAR and deploy on the Application Server.

swagger-ui1

 

To point by default to your own RESTful API with swagger annotations change the url in the index.html file.

swagger-ui2

Coach repeating table pagination toolkit

My first contribution to IBM BPM Wiki

A toolkit for paginating repeating tables in coaches. Not the only one available but the only one that does it just the way I like it.

http://bpmwiki.blueworkslive.com/display/samples/Coach+Table+Pagination+Toolkit

Using Teamworks code to write JavaScript

Here’s a little trick I learned recently and might be of use to someone else out there.

The basic requirement is to dynamically change the options in a combo box, based on data held in teamworks variables. More specifically, I want the options in ComboBox0 to change depending on what I select in ComboBox1. And I don’t want to wait for an AJAX round trip every time I change the selection.

So, suppose we have two local variables, both of which are lists of NameValuePair, and suppose they are initialised with data.

Here’s a pic of the variables:

And here’s the code that initialises them (lazy, I know, but it’ll do):

tw.local.itemList1 = new tw.object.listOf.NameValuePair();
tw.local.itemList1.insertIntoList(tw.local.itemList1.listLength, new tw.object.NameValuePair());
tw.local.itemList1.insertIntoList(tw.local.itemList1.listLength, new tw.object.NameValuePair());
tw.local.itemList1[0].name = "name1";
tw.local.itemList1[0].value = "value1";
tw.local.itemList1[1].name = "name2";
tw.local.itemList1[1].value = "value2";
tw.local.itemList2 = new tw.object.listOf.NameValuePair();
tw.local.itemList2.insertIntoList(tw.local.itemList2.listLength, new tw.object.NameValuePair());
tw.local.itemList2.insertIntoList(tw.local.itemList2.listLength, new tw.object.NameValuePair());
tw.local.itemList2[0].name = "name3";
tw.local.itemList2[0].value = "value3";
tw.local.itemList2[1].name = "name4";
tw.local.itemList2[1].value = "value4";

Then I create a coach with two Combo Box controls.

One control (ComboBox1) with manual data:

The other control (ComboBox0) bound to itemList1. I’m only binding it so it’s initially populated.

An finally, the interesting part, a block of custom html with the code to do the business:

<script type="text/javascript" src=<#=tw.system.model.findManagedFileByPath("jquery-1.4.2.min.js" , TWManagedFile.Types.Web).url #> > </script>
<script>
<#
 var optionList1 = [];
 var optionList2 = [];
 for (var i = 0; i < tw.local.itemList1.listLength; i++){
 var optionItem = [];
 optionItem.push("{\"text\":\"" + tw.local.itemList1[i].name + "\"", "\"value\":\"" + tw.local.itemList1[i].value + "\"}") ;
 optionList1.push(optionItem);
 }
 for (var i = 0; i < tw.local.itemList2.listLength; i++){
 var optionItem = [];
 optionItem.push("{\"text\":\"" + tw.local.itemList2[i].name + "\"", "\"value\":\"" + tw.local.itemList2[i].value + "\"}") ;
 optionList2.push(optionItem);
 }
#>
$(function(){
$('#ComboBox1').bind('change', function(e){
 var items = [];
 if($('#ComboBox1 option:selected').val() == "one"){
 items = [<#=optionList1#>];
 }
 else {
 items = [<#=optionList2#>];
 }
 $('#ComboBox0').empty();
 $.each(items, function(index, opt){
 $('#ComboBox0').append($('<option></option>').val(opt.value).html(opt.text));
 });
});
});
</script>

So, what exactly are we doing here?

First I’m importing JQuery, because I’m lazy and I find it helpful. But don’t get caught up with the JQuery stuff. It’s not the point of this post.

The code in question is divided into two parts:

First javascript that runs on the server, this is the chunk enclosed by pointy hashes (<# … #>).

And second, javascript that runs on the browser (the rest).

This is the conceptual leap I had to make, in Teamworks, not all javascript is equal, there’s the TW stuff and the plain old JS stuff. One executes on the server, before the coach is served, the other one is JS as we know it, for the browser.

With this in mind we can do all sorts of funky stuff.

And the best way to grok what’s happening here is to look at the source as served to the browser. So, I’m running the coach, I’m asking IE to show me the page source, and here’s the relevant bit of code:

<DIV class="customHTML" id="CustomHTML0"><script type="text/javascript" src=http://host.com/teamworks/webasset/2064.70c17442-cee9-46f5-857a-b58c9baf0e6a/W/jquery-1.4.2.min.js > </script>
<script>

$(function(){
$('#ComboBox1').bind('change', function(e){
 var items = [];
 if($('#ComboBox1 option:selected').val() == "one"){
 items = [{"text":"name1","value":"value1"},{"text":"name2","value":"value2"}];
 }
 else {
 items = [{"text":"name3","value":"value3"},{"text":"name4","value":"value4"}];
 }
 $('#ComboBox0').empty();
 $.each(items, function(index, opt){
 $('#ComboBox0').append($('<option></option>').val(opt.value).html(opt.text));
 });
});
});
</script></DIV>

The key thing to notice is how the items assignments work. The browser shows you how, within the conditional statement, the ‘items’ array is populated with JSON objects, but these JSON objects are actually created on the server, based on tw variables!

So basically what we are doing within the pointy hashes is generating the javascript that ultimately runs on the browser.

And once you crack that concept, well,  that’s where the fun starts, really :-)

Custom Coach Overrides and Single-Sign-On

At my client, we have defined and built a business process using IBM BPM 7.5.1, which includes coaches, and we display these coaches within our existing IBM Portal based solution.

There were a few challenges along the way in order to integrate BPM and Portal, including configuration of Single Sign On (SSO), and none of which actually caused real pain, apart from one little, almost trivial issue, which took us quite some time and effort to sort out.

We want the BPM coaches to look and feel just like all other Portal content, so we lifted the relevant CSS classes from the Portal solution and use them to override the default coach CSS. Of course that was fun to do, there was a lot of Firebug involved and eventually things were looking rather good, to the point where our coaches are now indistinguishable from any other portal content in the system.

But once we successfully completed the SSO configuration we lost the custom look and feel, which was puzzling, because we could see the customised coaches when testing the BPD from Process Designer, we could see them when we were running them on the playback server, but they reverted to the default coach appearance when going through Portal.

So eventually I had to roll up my sleeves, pump some Bassdrive into my skull and really dig in.

I fired up Fiddler, started monitoring HTTP traffic from my browser and discovered that my requests for the custom_coach.css file resulted in a 302 redirecting to the teamworks login page.

Interesting, SSO worked fine for everything but the CSS file. Teamworks did not see that particular request as coming from a trusted source.

And then I saw it. All other requests were to http://bpmhost.company.com:webport/teamworks/blah, but the CSS one was to http://bpmhost:appserverport/teamworks/csspath

When Teamworks serves the Coach, it sends to the browser links to a bunch of things, including the CSS file in question, and this link was being composed using the machine hostname, without the fully qualified domain, and with its app server port, rather than the web server port.

There are two problems with this. One is the port as it was forcing the browser to talk directly to a particular app server in the cluster, when we really want it to go through IHS, though this wasn’t breaking things. The other one is the non qualified hostname. This did break stuff.

The way SSO seems to work, when the browser receives the LTPA cookie, it is allowed to propagate it to servers in the same domain. So, after logging in to portalhost.company.com it was successfully requesting coach content to bpmhost.company.com but failing to retrieve the CSS file from bpmhost.

It turns out that there is a configuration file in the system folder for the bpm installation called 99Local.xml that controls such things. It defines URL prefixes for quite a number of moving parts, including Teamworks web resources, so a global replace of all hostnames to include the domain, and port to point at the web server, followed by a server restart did the trick.

 

 

BPM 7.5.1 – Waging war against cyclic dependencies

I’m wearing the hat of toolkit producer these days and I’m responsible for delivering a number of outbound JDBC Advanced Integration Services.

Another team member is working on another toolkit, which also implements JDBC AISs.

Both toolkits need the JDBC connector module to compile, and both toolkits will bundle the JDBC RAR (embedded adapter).

This arrangement creates two interesting problems. One at ID workspace compile time, the other at runtime deployment time.

When I create the outbound JDBC import on my toolkit implementation module, the JDBC connector project with the adapter RAR is created, and in order to compile the toolkit, this project has to be associated with it or its dependencies.

So I associated the connector with my toolkit. Splendid.

Now, when I bring into my workspace the other team member’s toolkit, which also has the connector associated with it, things get hairy. Process Center tells me the connector already exists in my workspace, and I say “fine, makes sense, don’t bother loading it to my workspace a second time then” and I deselected it from the projects to bring in.

But then, what happens is, the connector is associated with only one of the toolkits, and my ID workspace can’t build. Joy.

To make matters worse, the runtime starts moaning about duplicate contributions, along the lines of “CWYBC_JDBC is already installed”.

Now, we can install the rar standalone on the server and that would resolve the deployment issue, but we still need a way to build the toolkits.

So what we did is factored out the connector project into a third toolkit and made both my toolkit and the other team member’s depend on it.

This approach worked fine, the connector module is associated with a single toolkit and that solves the build problems, and, somewhat counter intuitively, even though both our toolkit implementation modules dependencies ask for the connector project to be embedded, the deployment works OK as well.

Business Objects and Lombardi Coaches

We’re working with IBM BPM 7.5 these days, or rather making it work, and a little idiosyncrasy of the product had us head-scratching for a few hours.

You may or may not know that the Lombardi part of the product uses variables to represent business process data. You would usually define variables local to the business process definition (BPD) and use data mappings to select what data will be used as inputs and outputs to the various activities within the process.

You can define your own data types (business objects) for these variables. So as way of example we defined an Individual, with string attributes for first and last names. Nothing fancy.

Then added a private variable to the BPD named individual of type Individual. Note how we did not specify a default value for the variable.

Suppose you now create a very simple BPD to use a UI Coach to populate the individual name and a simple javascript service to write the names to the log:

The log activity has a simple system script service to write the individual’s attributes to the system log:

When you run this, the first activity will let you populate the individual variable values:

What happens next is not that predictable. One would assume that the UI form took the user supplied values for fname and lname and used them to initialise and populate the individual.

Not quite.

Once the Coach task completed the process moved on to the log activity and failed. Crashed and burned.

The BPD instance has failed and the error is in the Log activity:

The details of the error are self explanatory, the individual variable is undefined:

It turns out that complex type variables need to be explicitly initialised, particularly when Coaches are involved!

http://publib.boulder.ibm.com/infocenter/wle/v7r1/topic/wle/modeling/topic/init_complex_vars.html

So, the IBM documented approach is to use a script activity to intitialise them:

But we also found out in our tests that giiving the variable default values also solves the problem, even if the values are empty strings:

So, hope this tip comes useful to someone else and you can look fwd to more posts on BPM 7.5

I’m sure we’re not going to be short of material for quite some time…

ttfn