Pencarian

Rss Posts

 

 

 

Quick introduction to Embeddability of GlassFish Open Source Edition 3.1

Mar 02, 2011

Embeddability of GlassFish is been around for quite some time now. In 3.1, the embeddable APIs have been revised. Most of the GlassFish community is already aware of the API revision, however I would like to briefly describe the revised APIs in this blog and welcome any feedback.

Embeddable API overview:

API JavaDocs are at http://embedded-glassfish.java.net/nonav/apidocs/

The APIs are briefly categorized as :

(a) Top level APIs (org.glassfish.embeddable) : Provides classes and interfaces necessary to embed GlassFish and perform lifecycle operations, application deployments and runtime configurations

(b) Scattered Archive APIs (org.glassfish.embeddable.archive) : Abstraction for a scattered Java EE archive (parts disseminated in various directories).

(c) Web Container APIs  (org.glassfish.embebdable.web, org.glassfish.embeddable.web.config) : Provides classes and interfaces necessary to programmatically configure embedded WebContainer and create contexts, virtual servers, and web listeners.

(d) Advanced pluggability (org.glassfish.embeddable.spi) : Provides classes and interfaces necessary to plugin a custom GlassFish runtime.

(e) EJB container APIs (javax.ejb.embeddable) : Refer "Embedded Server Guide" for EJB embeddable APIs

Basic examples of embedding GlassFish and deploying applications to embedded GlassFish:

These examples are can be run with either of the following jars in your CLASSPATH:

Full profile uber jar : http://download.java.net/maven/glassfish/org/glassfish/extras/glassfish-embedded-all/3.1/glassfish-embedded-all-3.1.jar
Web profile uber jar: http://download.java.net/maven/glassfish/org/glassfish/extras/glassfish-embedded-web/3.1/glassfish-embedded-web-3.1.jar
Installed GlassFish’s shell jar : $GF_INSTALLATION/lib/embedded/glassfish-embedded-static-shell.jar

Once you have ANY ONE of the above jar file with you, GlassFish can be embedded in your application by simply doing:

import org.glassfish.embeddable.*;

/** Create and start GlassFish */
GlassFish glassfish = GlassFishRuntime.bootstrap().newGlassFish();glassfish.start();

Let us say that you would like 8080 web container port to be started while embedding GlassFish, then you have to do this:

import org.glassfish.embeddable.*;

/** Create and start GlassFish which listens at 8080 http port */
GlassFishProperties gfProps = new GlassFishProperties();
gfProps.setPort("http-listener", 8080); // refer JavaDocs for the details of this API.

GlassFish glassfish = GlassFishRuntime.bootstrap().newGlassFish(gfProps);glassfish.start();

Or let us say that you have 3.1 installation and want to embed GlassFish domain1 in your application, then you can do:

import org.glassfish.embeddable.*;

/** Bootstrap the GlassFish runtime pointing to 3.1 installation */BootstrapProperties bsProps = new BootstrapProperties();bsProps.setInstallRoot(System.getEnv("GF_INSTALLATION"));GlassFishRuntime gfRuntime = GlassFishRuntime.bootstrap(bsProps);

/** Point GlassFish to domain1 */
GlassFishProperties gfProps = new GlassFishProperties();
gfProps.setInstanceRoot(System.getEnv("GF_INSTALLATION") + "/domains/domain1");
GlassFish glassfish = gfRuntime.newGlassFish(gfProps);

glassfish.start();

Note: If you have a custom domain.xml while embedding GlassFish, then you can use setConfigFileURI(String configFile) API of GlassFishProperties. JavaDoc has all the details.

Once you have the GlassFish embedded and is running, you may like to deploy a pre-built Java EE archive using the code below:

import org.glassfish.embeddable.*;

// Obtain the deployer from the glassfish which is embedded via the piece of code above.Deployer deployer = glassfish.getDeployer();

// syntax of deployment params are same as how they are passed to 'asadmin deploy' command.deployer.deploy(new File("simple.war"), "--contextroot=test", "--name=test", "--force=true");

// if you have no deployment params to pass, then simply do this:deployer.deploy(new File("simple.war")); 

If your archive is not pre-built, instead it’s components are scattered in multiple directories, then you may be interested in using the scattered archive APIs:

import org.glassfish.embeddable.*;import org.glassfish.embeddable.archive.*;

Deployer deployer = glassfish.getDeployer();

// Create a scattered web application.ScatteredArchive archive = new ScatteredArchive("testapp", ScatteredArchive.Type.WAR);// target/classes directory contains my complied servletsarchive.addClassPath(new File("target", "classes"));// resources/sun-web.xml is my WEB-INF/sun-web.xmlarchive.addMetadata(new File("resources", "sun-web.xml"));// resources/MyLogFactory is my META-INF/services/org.apache.commons.logging.LogFactoryarchive.addMetadata(new File("resources", "MyLogFactory"), "META-INF/services/org.apache.commons.logging.LogFactory");

deployer.deploy(archive.toURI())

Similarly, the scattered enterprise application (EAR type) can be deployed like this:

import org.glassfish.embeddable.*;import org.glassfish.embeddable.archive.*;

Deployer deployer = glassfish.getDeployer();

// Create a scattered web application.ScatteredArchive webmodule = new ScatteredArchive("testweb", ScatteredArchive.Type.WAR);// target/classes directory contains my complied servletswebmodule.addClassPath(new File("target", "classes"));// resources/sun-web.xml is my WEB-INF/sun-web.xmlwebmodule.addMetadata(new File("resources", "sun-web.xml"));

// Create a scattered enterprise archive.ScatteredEnterpriseArchive archive = new ScatteredEnterpriseArchive("testapp");// src/application.xml is my META-INF/application.xmlarchive.addMetadata(new File("src", "application.xml"));// Add scattered web module to the scattered enterprise archive.// src/application.xml references Web module as "scattered.war". Hence specify the name while adding the archive.archive.addArchive(webmodule.toURI(), "scattered.war");// lib/mylibrary.jar is a library JAR file.archive.addArchive(new File("lib", "mylibrary.jar"));// target/ejbclasses contain my compiled EJB module.// src/application.xml references EJB module as "ejb.jar". Hence specify the name while adding the archive.archive.addArchive(new File("target", "ejbclasses"), "ejb.jar");

deployer.deploy(archive.toURI())

Finally, towards the end of your application, you would like to stop/dispose your embedded GlassFish:

import org.glassfish.embeddable.*;

/** Stop GlassFish */
glassfish.stop(); // you can start it again.

/** Dispose GlassFish */glassfish.dispose(); // you can not start it again. But you can embed a fresh glassfish with GlassFishRuntime.bootstrap().newGlassFish()

More Examples:

If you checkout https://svn.java.net/svn/glassfish~svn/trunk/v3/tests/embedded you will find many more examples which cover embeddable web container APIs also.

Feedback:

If you have any feebback on the APIs, please send them to dev@glassfish.java.net or dev@embedded-glassfish.java.net

More on Web Sockets and HTML5 in Glassfish

Oct 14, 2010

 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 is BoardMirror.

 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 communication parallel 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 special clear 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
Attachment Size
boardmirror.jpg 132 KB
BoardMirror.zip 27.12 KB

OSGi-enabled Java EE Applications using NetBeans and GlassFish

Oct 04, 2010

Content available at: http://blogs.sun.com/arungupta/entry/screencast_32_osgi_enabled_java