1 2 3 4 Previous Next

spericas

59 posts

In the first blog entry of this series, we showed how to configure a message body reader (MBR) on an instance of Client. Specifically, our example required the registration of an MBR to convert an application/json representation of theMoney bean. 

Client client = ClientFactory.newClient();
client.configuration().register(MoneyMBR.class);

As stated in that blog entry, a Client instance can be used to create one or more instances of Target, which in turn, can be used to create and execute one or more requests. As we shall see shortly, targets can be used to create other targets as well. An interesting aspect of the API is that configurations can be inherited by descendants: thus, a target will inherit a client or another target's configuration and a request will inherit a target's configuration. Inheriting a configuration from a parent object creates a deep copy, so updates to descendants' configurations have no impact on a parent's configuration. 

From the point of view of the Client API, instances ofClient are heavyweight while instances of Targetare lightweight. Generally speaking, a single instance ofClient should be created and configured with the providers that are needed for the targets created from it. Different targets can be created with different configurations to access the various services needed by the application. Lastly, a particular request can be further configured if so required. Lets take a look at an example in which two targets are created, one from aClient and one from another Target

// Get client and register MyProvider1
Client client = ClientFactory.newClient(); 
client.configuration().register(MyProvider1.class);

// Create atm and register MyProvider2
// Inherits MyProvider1 from client
Target atm = client.target("http://.../atm"); 
atm.configuration().register(MyProvider2.class);

// Create balance and register MyProvider3
// Inherits MyProvider1, MyProvider2 from atm
Target balance = atm.path("balance"); // new instance 
balance.configuration().register(MyProvider3.class);

In this example, an instance of Client is created and a provider registered; a target atm is created from it and a second provider registered and, finally, a target balanceis created from atm with a third provider. Note that instances of Target are immutable with respect to their URI (but not their configurations!), thus the call toatm.path("balance") creates a new instance. 

Configuration of an individual request takes an extra step given that request creation and request configuration operate on different method chains. For example,

Invocation.Builder b = client.target("http://.../atm/withdrawal") 
                  .queryParam("card", "111122223333") 
                  .queryParam("pin", "9876") 
                  .request("application/json");
b.configuration().register(MoneyMBR.class);
Money mon = b.post(text("50.0"), Money.class);

In addition to provider registration, a Configurationobject can be used to set properties (name-value pairs) as well as features. A feature is a user-provided class implementing theFeature interface that can be used to register providers, properties and other features as a single unit. We will take a look at features in later blog.

This is a follow-up to last week's blog in which we introduced the new Client API in JAX-RS 2.0. In that blog, we defined a simple AtmServiceand showed how to use the Client API to access its resources. For example, the following code was used to check an account's balance:

Client client = ClientFactory.newClient();

String bal = client.target("http://.../atm/balance")
                   .queryParam("card", "111122223333")
                   .queryParam("pin", "9876")
                   .request("text/plain").get(String.class);

 The Client API uses a fluent style with which a request can be built by chaining method calls. The purpose of the last method call in the chain is threefold: (i) it specifies the HTTP method (ii) indicates the Java type requested for the response and (iii) executes the request. But what if we need to build a request for execution at a later time? What if we need to separate creation from execution? Perhaps we need to store a request in a data structure, pass it to another module in the application, etc. This is the purpose of the Generic Interface: it enables requests to be first-class citizens in our applications.

Let us re-write the requests in last week's blog using the Generic Interface:

Invocation inv1 = client.target("http://.../atm/balance")
                        .queryParam("card", "111122223333")
                        .queryParam("pin", "9876")
                        .request(“text/plain”).buildGet();

Invocation inv2 = client.target("http://.../atm/withdrawal")
                        .queryParam("card", "111122223333")
                        .queryParam("pin", "9876")
                        .request("application/json").buildPost(text("50.0"));

The key difference in this example is the use of thebuildXXX() methods. This family of methods returns an instance of Invocation that can be executed at a later time. Note that the decision of the Java type to be returned is also delayed, and it is now the responsability of thesubmitter rather than the builder.

We can now store these Invocation objects or pass them around or even execute them multiple times if needed. The following example stores the invocations created above in a collection and invokes them via a map operation:

Collection<Invocation> invs = Arrays.asList(inv1, inv2);

Collection<Response> ress =
    Collections.transform(invs,
        new F<Invocation, Response>() {
            public Response apply(Invocation inv) {
                return inv.invoke(); 
            }
        });

The invoke() method without any arguments returns an instance of Response. Next week we are going to dive into the topic configurations and configuration inheritance. 

spericas

JAX-RS 2.0 - Client API Blog

Posted by spericas Oct 14, 2011

The Early Draft for JAX-RS 2.0 (JSR 339) has recently been submitted for publication. There are number of interested features in the new draft, and I'd like to start with the new Client API. The Client API is used to access Web resources. It provides a higher-level API than HttpURLConnection, and perhaps more importantly, integration with JAX-RS providers.

Let us start by defining a simple ATM service using JAX-RS:

@Path("/atm")
public class AtmService {

    @GET
    @Path("/balance")
    @Produces("plain/text")
    public String balance(@QueryParam("card") String card,
                          @QueryParam("pin") String pin) {
        ...
    }

    @POST
    @Path("/withdrawal")
    @Consumes("plain/text")
    @Produces("application/json")
    public Money withdrawal(@QueryParam("card") String card,
                            @QueryParam("pin") String pin, Double amount) {
        ...
    }
}

 This web resource class defines a simple ATM service with two subresources to retrieve an account's balance and to withdraw money. Accessing these resources requires the client to provide a card number and a PIN. Naturally, the withdrawal subresource also requires the client to provide an amount. The actual implementation of the service isn't important for our purposes.

 The entry point to the Client API is the Clientclass. Using this class we can create one or more Targetinstances. From each Target instance we can create one or more requests. The fluency of the API somewhat blurs all these steps, but it's important to understand them in order to write better code. The following code snippet can be used to check an account's balance:

Client client = ClientFactory.newClient();

String bal = client.target("http://.../atm/balance")
                   .queryParam("card", "111122223333")
                   .queryParam("pin", "9876")
                   .request("text/plain").get(String.class);

 In this example, the target is created from a URI and later extended by adding two query parameters. After that, a request is obtained and a resource's representation selected, namely "text/plain". Finally, the HTTP access method is specified (GET) as well as the Java type to be used for the response. Note that JAX-RS provides a built-in Message Body Reader (MBR) to convert a "text/plain" representation into a Java String, so no further configuration is required.

The following code shows how to interact with the withdrawal subresource:

Money mon = client.target("http://.../atm/withdrawal") 
                  .queryParam("card", "111122223333") 
                  .queryParam("pin", "9876") 
                  .request("application/json")
                  .post(text("50.0"), Money.class);

Note that the post() method has two parameters: the request entity and the Java type to be used for the response. The variant text() is a convenient way to specify an entity of type "text/plain" while at the same time providing a value for it, i.e. the string "50.0". The execution of this request will require the conversion of the Java string "50.0" into a "text/plain" representation, for which a built-in Message Body Writer or MBW is already provided by JAX-RS, as well as a MBR to convert a representation of type "application/json" into an instance of theMoney class. 

Given that the Money class is not known to the JAX-RS runtime, the application must provide an MBR for it. This can be done by accessing a configuration object. Most of the Client API classes are configurable; let us show how to register this provider using our Client instance.

Client client = ClientFactory.newClient();
client.configuration().register(MoneyMBR.class);

 As we shall see in later blog entries, registration of providers is also possible on targets and requests, and perhaps even more interestingly, configurations can be inherited.

 In my last blog entry, I described VideoSharing which is an application that uses Web sockets to remote UI events and enable participants to control HTML5 video players remotely. Today, I'd like to share with you a similar type of collaboration application, but this time one that uses other HTML5 features: namely, 2D canvases and client SQL databases. The name of this application isBoardMirror.

 Rather than sharing a video object, like in VideoSharing, BoardMirror shares a canvas object and lets participants draw figures on it. For the sake of simplicity, only two figures can be drawn on a canvas: rectangles and circles. These figures are drawn in a random location inside the canvas and using a random color. Here is a screenshot of the application:

The mirror or collaboration part of the application is the result of copying these figures in all the other canvases that are connected to the server. And, of course, we use Web sockets for this purpose. A Web socket is nothing more than a socket connection that can be established between a (modern) Web browser and a Web container. It provides a low-latency, bi-directional communicationparallel to the HTTP channel. It is Ajax/Comet on steroids.

In addition, the BoardMirror application has the ability to store your work of art in a local SQL database in the browser. This is done using one of the new APIs in HTML5. In fact, HTML5 has two options for storing data on the client side: Web Storage and Web SQL Databases. The former is implemented by most browsers and can store name-value pairs; the latter, as suggested by its name, gives you full SQL storage but it's only implement in some browsers.

Let me start describing the canvas and 2D APIs first. As in the VideoSharing application, we use a JSF facelet to define our main page:

    <html ...>
      ...
      <h:body>
          <h5:canvas width="600" height="400"/>
          <h:outputScript library="js" name="json2.js" target="head"/>
          <h:outputScript library="js" name="app.js" target="head"/>
          <br/>
          <input type="button" value="Rectangle" onclick="APP.drawRectangle()"/>
          <input type="button" value="Circle" onclick="APP.drawCircle()"/>
          <input type="button" value="Clear" onclick="APP.clearCanvas(true)"/>
          <input type="button" value="Load" onclick="APP.loadCanvas()"/>
          <input type="button" value="Save" onclick="APP.saveCanvas()"/>
      </h:body>
   <html>

where the h5:canvas element is defined in the same way as h5:video in the VideoSharing blog. What follows is a JavaScript method that draws a rectangle, sends the rectangle to be drawn remotely and stores the rectangle in a local data structure so that it can saved in a local database, if so requested by the user.

    drawRectangle: function() {
        // Get access to canvas and draw rectangle
        var canvas = this.getCanvas();
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = this.random.color();
        var width = this.random.number(40, 100);
        var height = this.random.number(40, 100);
        var x = this.random.number(0, canvas.width - width);
        var y = this.random.number(0, canvas.height - height);
        ctx.fillRect(x, y, width, height);

        // Mirror rectangle via web sockets and store in array
        var shape = {type: "rectangle", x: x, y: y, width: width,
            height: height, color: ctx.fillStyle};
        this.network.send(JSON.stringify(shape));
        this.shapes[this.shapes.length] = shape;
    },

The code that draws the rectangle should be self explanatory, yet another 2D API! The last three lines of code are there to: create a shape object, send the shape over the network and store the shape in an array, respectively. The network object is initialized in the same way as in the VideoSharing application; only it's onmessage handler is different:

    websocket.onmessage = function (evt) {
        var shape = JSON.parse(evt.data);
        if (shape.type == "clear") {
            APP.clearCanvas(false);
        } else {
            APP.mirrorShape(shape, false);
        }
    };

Since a canvas can be remotely cleared, there's a specialclear shape type that triggers such a remote event. For all the other shapes (real shapes, that is), the mirrorShape() method is called. This method simply draws the shape in the local canvas (without resending the shape over the network again!).

As stated above, this application also supports storing your canvas in a local database. This is done by storing all the shapes in JSON format. The database logic is implemented in the object returned by the database function shown next.

    var database = function (db) {
      return {
        error: function (err) {
            alert(err.message);
        },
        initialize: function() {
            if (window.openDatabase) {
                db = openDatabase('shapes', '1.0', 'All Shapes', 64 * 1024);
                db.transaction(
                    function (tx) {
                        tx.executeSql('CREATE TABLE shapes (id UNIQUE, shape STRING)')
                    });
                this.load();
            } else {
                alert("Your browser does not support SQL databases");
            }
        },
        load: function() {
            db.transaction(function (tx) {
                tx.executeSql('SELECT * FROM shapes',
                [],
                function (tx, results) {
                    for (var i = 0; i < results.rows.length; i++) {
                        var shape = JSON.parse(results.rows.item(i).shape);
                        APP.shapes[i] = shape;
                    }
                    APP.refreshCanvas();
                },
                this.error)
            } );
        },
        save: function() {
            db.transaction(function (tx) {
                tx.executeSql("DELETE FROM shapes");
                for (var i = 0; i < APP.shapes.length; i++) {
                    tx.executeSql('INSERT INTO shapes (id, shape) VALUES (?, ?)',
                    [i, JSON.stringify(APP.shapes[i])], this.error);
                }
                alert("Canvas has been saved into a local database");
            });
        }
      }
    };

In the initialize() method, the SQL database is opened and a table is created to store the shapes. If the table exists, an error will be ignored since no error handler is provided in the call to tx.executeSql(). The load() method runs a select query without any parameters (notice the "[]") and provides a results handler that parses and stores the shape in the array. The save() method clears the table and then inserts the shape one by one into the database. Note how parameters are passed to fill in the value placeholders denoted by "?" in the SQL string. This should be familiar to anyone that has written JDBC code before.

One interesting aspect of the SQL API is that, following a pattern that is common in Javascript, all operations are asynchronous. This is why functions are passed to methods like db.transaction() and tx.executeSql(). However, the order in which SQL statements are executed follows the execution order of the calls to tx.executeSql(). That is, statements are always queued and run in order —and this is why methods like save() above work.

The Web sockets code in Glassfish that supports this application is identical to that in the VideoSharing application. For more information, the reader is referred to the VideoSharing screencast or to the attached source code bundle. Please note that Web sockets are not enabled by default in Glassfish. To enable them you must execute the following single-line command on your domain:

asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.
websockets-support-enabled=true

It's been several months since my last blog! I have recently re-joined the Glassfish team at Oracle and I'm currently looking at Web tier technologies like Web sockets and HTML5. In this blog, I'd like to show you a simple Web application in which you can control an HTML5 video object remotely. The basic idea is to intercept video events like 'play', 'pause' and 'seeked' and remote them using Web Sockets to control another player. Although there may be some use cases for an application like this (such as coaching), the real objective of this exercise is to show the low latency of the Web sockets implementation in Glassfish. So let's get started!

Here is a screenshot of the application:

A Web socket is nothing more than a socket connection that can be established between a (modern) Web browser and a Web container. It provides a low-latency, bi-directional communication "parallel" to the HTTP channel. It is Ajax/Comet on steroids. No hacks, no jumping through loops, nothing. It has the potential of redefining what we understand as a Web application lifecycle given it's low latency and always connected characteristics (but more about this on a future blog).

Our Web application comprises of a single page and some JavaScript code. We use JSF 2.0 facelets to define the main page:

 <html ...>
   <h:head>...</h:head>
   <h:body>
      <p>Let's share this video:</p>
      <p><h5:video height="360" controls="true" src="PBIRbest.m4v"/></p>
      <h:outputScript library="js" name="json2.js" target="head"/>
      <h:outputScript library="js" name="app.js" target="head"/>
   </h:body>
 </html>

 The tag h5:video is defined by the following custom JSF component:

<html ...>
  <cc:interface>
    <cc:attribute name="src" required="true"/>
    <cc:attribute name="controls" required="false"/>
    <cc:attribute name="width" required="false"/>
    <cc:attribute name="height" required="false"/>
  </cc:interface>
  <cc:implementation>
   <video src="#{cc.attrs.src}" controls="#{cc.attrs.controls}" 
      width="#{cc.attrs.width}" height="#{cc.attrs.height}"></video>
  </cc:implementation> 
</html>

 Defining a new h5:video tag (instead of using HTML5's video tag directly) is not a requirement for the application, but it does provide a clear separation and makes the example more readable (a separate component can be used to insert additional JavaScript code, if needed). Note the inclusion of the JavaScript libraries: json2.js and app.js. The former provides us with JSON.stringify() and JSON.parse(); the latter is where our client-side code resides. Let's have a look at that.

 Let us define a JavaScript function that returns a network object (i.e., a closure). This network object has two methods: initialize() and send(). The former opens a Web socket and registers listeners for incoming messages; the latter is for sending outgoing messages.

var network = function (websocket) {
    return {
        initialize: function() {
            var url = 'ws://' + document.location.host + document.location.pathname + 'videosharing';
            websocket = new WebSocket(url);
            websocket.name = APP.id;
            websocket.onopen = function() { };
            websocket.onmessage = function (evt) {
                var command = JSON.parse(evt.data);
                if (command.type == "pause") {
                    APP.pauseVideo();
                } else if (command.type == "play") {
                    APP.playVideo();
                } else if (command.type == "seeked") {
                    APP.seekVideo(command.currentTime);
                } else {
                    alert("Unknown command " + command);
                }
            };
            websocket.onclose = function() { };
        },
        send: function(shape) {
            websocket.send(shape);
        }
    }
};

 First, a Web socket is created from a URL constructed using the main page's host, pathname and "/videosharing". Notice the use of the "ws://" protocol. Next, listeners for onopen, onmessage and onclose events are registered. The onmessage listener parses an incoming command, determines its type and calls the appropriate method in out application object APP. This is how UI events are remoted. The send() method is used to remote events that are local to this window instance. 

The network's initialize() method is called by APP.initialize(). This method also registers listeners for all local video events for remoting purposes as shown next.

    initialize: function () {
        APP.network.initialize();

        var video = APP.getVideo();
        video.addEventListener('play', 
            function (event) { 
                var command = { type: "play" };
                APP.network.send(JSON.stringify(command));
            },
            false);
        video.addEventListener('pause',
            function (event) {
                var command = { type: "pause" };
                APP.network.send(JSON.stringify(command));
            },
            false);
        video.addEventListener('seeked',
            function (event) {
                var command = { type: "seeked",
                                currentTime: APP.getVideo().currentTime };
                APP.network.send(JSON.stringify(command));
            },
            false);
    }

Note how commands are constructed, stringified and remoted using to the network object. All commands have a type field; the 'seeked' command also includes a currentTime field in order to seek remote video objects to the correct location. The rest of the client code is uninteresting so lets look at the server side pieces.

 The server side API provided in Glassifish 3.1 for Web sockets is straightforward. First, we extend the class WebSocketApplication and override methods createSocket() and onMessage() as shown next.

public class VideoSharingApplication extends WebSocketApplication {

    @Override
    public WebSocket createSocket(NetworkHandler handler,
            WebSocketListener... listeners) throws IOException {
        return new VideoSharingWebSocket(handler, listeners);
    }

    @Override
    public void onMessage(WebSocket socket, DataFrame frame) throws IOException {
        final String data = frame.getTextPayload();
        for (WebSocket webSocket : getWebSockets()) {
            try {
                if (socket != webSocket) {
                    webSocket.send(data);
                }
            } catch (IOException e) {
                e.printStackTrace();
                webSocket.close();
            }
        }
    }
}

The onMessage() method handles socket frames by extracting the data in the frame and broadcasting it to all other Web socket clients. The createSocket() method simply creates an instance of our app's Web socket which, in this application, it is very simple.

public class VideoSharingWebSocket extends BaseServerWebSocket {
    public VideoSharingWebSocket(NetworkHandler handler,
            WebSocketListener... listeners) {
        super(handler, listeners);
    }
}

The last piece in this puzzle is the registration of our application in the WebSocketEngine. This can be accomplished using the init() method in a servlet as shown below.

public class WebSocketsServlet extends HttpServlet {

    private final VideoSharingApplication app = new VideoSharingApplication();

    @Override
    public void init(ServletConfig config) throws ServletException {
        WebSocketEngine.getEngine().register(
            config.getServletContext().getContextPath() + "/videosharing", app);
    }
}

Of note, this server-side API is still evolving and is subject to change and improvements in upcoming versions of Glassfish. A source bundle for this sample is attached and includes a NetBeans 6.9.1 project file. You must use Glassfish Server Open Source Edition 3.1 and a browser that supports Web sockets (as well as the codec for the video that you use!). In my project, I include an MPEG-4 video and Safari 5.X on a Mac. 

During the last few weeks, Marc H., Paul S. and myself have been exploring some ideas to support Hypermedia in Jersey. The outcome of this investigation is an experimental implementation that is available in Jersey's trunk (module version 1.2-SNAPSHOT). Exactly what it means to support hypermedia is still an area of research, and some other implementations of JAX-RS (notably RESTfulie) have also proposed APIs for it.

The REST architectural style, as defined by Roy Fielding in his thesis, is characterized by four constraints: (i) identification of resources (ii) manipulation of resources through representations (iii) self-descriptive messages and (iv) hypermedia as the engine of application state. It is constraint (iv), hypermedia as the engine of application state or HATEOAS for short, that is the least understood and the focus of our investigation. 

It has been identified by other authors that there areactions that cannot be easily mapped to read or write operations on resources. These operations are inherently more complex and their details are rarely of interest to clients. In our work, we introduce the concept of action resources. An action resource is a sub-resource defined for the purpose of exposing workflow-related operations on parent resources. As sub-resources, action resources are identified by URIs that are relative to their parent. For instance, the following are examples of action resources:

 http://.../orders/1/review
 http://.../orders/1/pay
 http://.../orders/1/ship

 for purchase order “1” identified by http://.../orders/1.

A set of action resources defines—via their link relationships—a contract with clients that has the potential to evolve over time depending on the application’s state. For instance, assuming purchase orders are only reviewed once, the review action will become unavailable and the pay action will become available after an order is reviewed.

The notion of action resources naturally leads to discussions about improved client APIs to support them. Given that action resources are identified by URIs, no additional API is really necessary, but the use of client-side proxies and method invocations to trigger these actions seems quite natural. Additionally, the use of client proxies introduces a level of indirection that enables better support for server evolution, i.e. the ability of a server’s contract to support certain changes without breaking existing clients.

The Jersey extensions that we implemented were influenced by the following (inter-related) requirements:

  • HATEOAS: Support for actions and contextual action sets as first-class citizens.
  • Ease of use: Annotation-driven model for both client APIs and server APIs.
  • Server Evolution: Various degrees of client and server coupling, ranging from static contracts to contextual contracts.

Please refer to the Hypermedia Chapter in the User's Guide for more information. Note that the proposed API is still experimental and will possibly evolve in unforeseen (and incompatible) ways. Your feedback will be extremely valuable in shaping future versions of this API.

The Sun Mobility Platform Telematics demo, co-developed by Oracle and Sun, was showcased at Oracle Open World this week. It was shown as part of a presentation on Monday and at the Berkeley DB (BDB) booth for the rest of the week. The demo shows a simulation of vehicles synchronizing events collected on a local database (BDB) to a back-end Oracle database. Events collected from vehicle sensors are stored in an embedded BDB database and periodically synchronized; in addition, critical events are reported immediately using the dynamic capabilities of the Mobility Platform. A JavaFX front-end was developed to increase the demo's coolness factor.

Vehicle sensors generate 3 types of events: INFORMATIONAL, DIAGNOSTICS and CRITICAL. All events are recorded in an embedded BDB database in the vehicle and synchronized periodically using the Sun Mobility Platform (SyncML over HTTP). Critical events are reported immediately to the server which responds synchronously by acknowledging the event. After some processing time (a random factor in the simulation) the server contacts the vehicle with an action associated with a previously acknowledged critical event. This communication is done using restful WS (Jersey) and HTTP. The vehicle uses the lightweight HTTP server in JDK 6; the Mobility Platform gateway runs on either Glassfish or Weblogic.  

WATCH THE SCREENCAST

It is just a matter of time when all the vehicles on the road will be connected to their factories and service centers. When there will be no need to make an appointment with a service center to diagnose a problem. Instead, problems will be diagnosed well ahead of time based on the data collected by the vehicles and parts will be ready at a service center when the vehicle arrives ...

Please contact us at mep-feedback@sun.com if you would like to get more details including an early access version of the latest Mobility Platform software for evaluation.

As Ryan described in his recent blog, we now have support for Java SE clients in the Mobility Platform. Mahesh Sharma, a student and Sun ambassador in India, has been working on a port of this library to the Android platform. As part of his work, Mahesh also re-wrote the UI of our MusicDb demo and proved that Android is a great plaform to run SGMP applications.

With Mahesh's work we now have full support for MCBO clients on Android. Just like in Java SE, the MCBO API remains unchanged, so any Java ME application that uses our sync library can be ported to Android by simply re-writing its UI. Here are a couple of screenshots from Mahesh's MusicDb application on the Android emulator:

   

We have also tried this application on an actual Android device and the application's usability and performance did not disappoint. If you are interested in trying out these new features and providing feedback, please contact us at mep-feedback@sun.com and we can provide an early access release of the Mobility Platform for a limited evaluation period.

The Mobility Platform team has been working on a number of new features and enhancements since the release of GlassFish Mobility Platform v1.1 in February 2009. One of those features is the ability to run the Mobility Platform software on top of Glassfish ESB. There is a clear synergy between the Mobility Platform and Glassfish ESB. The former is about mobilizing the enterprise, while the latter is about integration of enterprise and legacy information systems. By running the Mobility Platform on top of Glassfish ESB, as opposed to just Glassfish, it is possible to write enterprise connectors that sit on the enterprise bus and can mobilize all sorts of information systems.

The Mobility Platform v1.1 supports two types of enterprise connectors: based on ECBO or JAX-RS. ECBO connectors are deployed as Java EE connectors and are co-located with the sync engine on a Glassfish instance. JAX-RS connectors are web applications that can be deployed on any Glassfish instance and interface with the sync engine via a JAX-RS connector bridge (which is a Java EE connector written using the ECBO). To enable integration with the ESB service bus, we have developed a new connector bridge for JAX-WS. This connector translates ECBO requests into JAX-WS requests to a connector implemented as a web service endpoint. The exact details of this web service definition is beyond the scope of this blog; it suffices to say that the web service defines a method for each of the four CRUD (create, retrieve, update and delete) operations involved in a synchronization session.

Given that enterprise connector is now simply a SOAP-based web service, it is possible to implement it using tools and languages available in Glassfish ESB. In particular, it is possible to use BPEL and the excellent graphical tools in Glassfish ESB to develop an enterprise connector. In what follows, will we'll investigate the development of a MusicDb connector using these tools. For more information on the MusicDb connector please refer to this blog entry.

Every enterprise connector implemented in BPEL will need access a serialization service, which is specific to each connector, and is responsible for serializing and deserializing business objects according to the contract that was agreed with client applications. The serialization service is implemented as a web service and listed as a partner link in the BPEL process. In addition, since the MusicDb connector stores business objects in a relational database, there is a database binding component (BC) which is also partner link in the BPEL process definition. The overall flow of the process is: loop until the terminate message is received and execute commands using the serialization service and the database binding component as needed to implement the semantics of each message. The following is a graphical representation of this BPEL process:


http://blogs.sun.com/santiago/resource/bpel.gif 

The box on the left represents the WSDL implemented by all enterprise connectors that use JAX-WS. The two boxes on the right correspond to the database binding component and the serialization service, from top to bottom. The middle box is a graphical representation of the BPEL process flow outlined above.

If you are interested in trying out any of these new features and providing feedback, please contact us at mep-feedback@sun.com and we can provide an early access release of the Mobility Platform for a limited evaluation period.

As many of you out there, I wanted to explore the world of JavaFX. After looking at a few demos here and there, I decided it was time to write some code; there is simply no better way to learn a new technology like this than writing something. A quick glance over the language primitives and programming model made my decision very easy: a simple video game was best choice to learn JavaFX!

I am not a game programmer, but just like many of you, I wrote a couple of simple games in the past. So it took some GIFs from an old game (which are share by most Space Invader-like games out there!) and put together a very simple game in 400+ likes of JavaFX code. I was amazed by the result. JavaFX is a incredible powerful platform, so I decided to share with you the source code to hopefully encourage you to get started as well. Here's what the game looks like:

jnebulafx.jpg


 

It should be pretty easy to port this game to JavaFX Mobile, but I haven't had the time myself (so if you do it, please send it to me!). The controls are straightforward: UP ARROW, DOWN ARROW, LEFT ARROW, RIGHT ARROW and SPACE (fire).

I believe the source code is pretty easy to read, so I hope a quick glance at it we'll get you intrigued and help you get started. Have fun!

DOWNLOAD JNebulaFx SOURCE CODE HERE

This is a follow-up to the blog "Developing SGMP Connectors using JAX-RS" where I focus on how to deploy an SGMP connector developed using JAX-RS. Once again, rather than bore you with words, I will attempt to entertain you with a screencast. The screencast will show you how to build your connector using Netbeans and how to deploy and configure your connector using the GlassFish admin console.

WATCH SCREENCAST

Last week we announced the availability of version 1.1 of the Sun GlassFish Mobility Platform (SGMP), formerly known as Mobile Enterprise Platform (MEP). In the previous version, a connector could only be developed using the ECBO API; in this new version, a connector can also be developed using JAX-RS. The attached screencast shows how to develop a JAX-RS connector using Netbeans 6.5 and Maven. Next time we'll focus on how to deploy a JAX-RS connector.

WATCH SCREENCAST

In case you missed it, the call is open for JavaOne 2009 submissions and the deadline is December 19th, 2008. Contrary to the last few years, the conference will be in early June in 2009, so there's plenty of time for your travel plans but not a lot of time to prepare your abstracts!

One of the topics listed in the announcement is Mobility. This includes core ME technologies, platforms, languages, etc. and also enterprise mobility, a topic that I'm particularly interested these days. So whether you implemented your enterprise mobility solution using the Sun Java System Mobile Enterprise Platform or in some other way, we'd like to hear from you. Let's make the enterprise mobility track shine at JavaOne in 2009!

In this installment of the Developing MEP Connectorsseries we focus on the development cycle of a MEP connector, including source code generation from a template, compilation and deployment. The recommended tools for developing MEP connectors are NetBeans 6.1 (or later) and Maven 2.0.8 (or later), although developing under other IDEs should be straightforward given that Maven is the primary tool that we use.

The first step is to download and install the FCS version of MEP which is available here. The installation process will set up Glassfish first and MEP second. In what follows, we'll assume that MEP is installed under$AS_HOME/domains/mep, the default location.

In order to build a new connector, we start by installing a dependency in our local Maven repository. We can do that by cding into $AS_HOME/domains/mep/lib and using the mvncommand as shown below.

>> cd $AS_HOME/domains/mep/lib
>> mvn install:install-file -DgroupId=com.synchronica.mep 
   -DartifactId=ds-jcr-connector -Dversion=3.1.31 -Dpackaging=jar 
   -Dfile=ds-jcr-connector-3.1.31.jar

With that dependency installed, we can now start NetBeans and verify that we have the Maven plugin installed by going to Tools -> Plugins -> Installed and looking for a plugin named "Maven". If it isn't installed, search for it under Available Plugins and install it.

The next step is the generation of our MEP connector template. This step uses a Maven archetype available from Java.net. To create the project go to File -> New Project -> Maven -> Maven Project and click "Next". From the list of Maven archetypes, open the "Archetypes from remote Maven Repositories" entry and select "MEP Connector Archetype (JAR)", the first archetype listed for MEP.


mep-archetype.jpg



 

After clicking "Next", simply name your project and click "Finish". The first time you generate a project, it will take a few minutes for Maven to warm up your local repository cache. Subsequent operations that use Maven will be much faster. Once the operation is completed, you should be able to browse the generated source code under "Source Packages" and build your project under the IDE (naturally, the connector generated by the archetype is just a template and does not implement any business logic). The resulting jar file, generated as part of the building process and available in the target directory, can be simply copied to$AS_HOME/domains/mep/lib. For more information on how to configure a connector, the reader is referred to the MEP Administration Guide. Happy synching!

In the two installments of this series we've looked at the architecture of a MEP connector, discussed the main abstractions of the ECBO (Enterprise Connector Business Object) API and showed an example of a MusicAlbum business object. In this third part, we'll focus on the MusicAlbumProvider class which is needed to complete the implementation of the connector to access music albums from a JDBC database.

The complete source code for the MusicDB enterprise connector is included in the MEP client bundle available for download here. In the directory where you unzipped the client bundle, look for it under sjsmep-client-1_0-fcs/samples/ecbo/.

As explained earlier in this series, the ECBO API provides two essential abstractions: BusinessObject andBusinessObjectProvider. Due to the nature of the synchronization process, every enterprise connector must provide the ability to CRUD (create, retrieve, update and delete) business objects. The BusinessObject abstraction provides support for CUD, i.e. each object provides the ability to create, update and delete itself as shown in Part II. The R operation, or retrieve, is implemented by the BusinessObjectProvider. Here is the implementation of the R operation inMusicAlbumProvider, a class extendingBusinessObjectProvider.

    @Override
    public List<MusicAlbum> getBusinessObjects() {
        Statement stmt = null;
        List<MusicAlbum> albums = null;
        
        try {
            stmt = sqlConnection.createStatement();
            
            // Read all music albums and store them in array
            albums = new ArrayList<MusicAlbum>();
            ResultSet rs = stmt.executeQuery(
                    "SELECT * FROM album WHERE username = '" + username + "'");
            while (rs.next()) {
                MusicAlbum album = new MusicAlbum(this);
                album.setName(rs.getString(1));
                album.setArtist(rs.getString(2));
                album.setDatePublished(rs.getString(3).replace("-", ""));
                album.setRating(rs.getInt(4));
                albums.add(album);
            }
            rs.close();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } finally {
            if (stmt != null) {
                try { stmt.close(); } catch (Exception e) { /* ignore !*/ }
            }
        }        
        return albums;
    }

The getBusinessObjects() method simply queries the database for all albums that are owned by the user that logged in into the connector and returns them as aList<MusicAlbum>.

In addition to providing support for the R operation, a business object provider being the entry point to the connector, should also provide implementations for the lifecycle methodsinitialize() and terminate(). These methods are called at the beginning and at the end of a synchronization session. In most connectors, these methods are used to connect to and disconnect from an enterprise system; in the MusicDB connector, in particular, they are used to connect and disconnect from the database as shown below.

    @Override
    public void initialize() {
        try {
            Context jndiContext = new InitialContext();
            DataSource ds = (DataSource) jndiContext.lookup(MUSICDB_JNDI_DATASOURCE);
            sqlConnection = ds.getConnection();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } 
    }
    @Override
    public void terminate() {
        try {
            if (sqlConnection != null) {
                sqlConnection.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

Contextual information related to the active synchronization session (e.g., session id or user logged in) can be access via calling getSessionContext(). Finally, each connector may define its own transaction manager by overriding the methodgetTransactionManager(). For more information on these features, the reader is referred to the MEP online documentation.

If you're interested in developing a MEP connector, I suggest that you start by using Maven's MEP Connector Archetype (JAR). If you're a Netbeans user, simply go to File -> New Project -> Maven -> Maven Project and select it from the list. Happy synching!

Filter Blog

By date: