GlassFish OSE 3.1 is out with WebSocket support included

By now you likely know that we released 3.1 of GlassFish today.  It's all over twitter with much of the jubilation around the return of clustering and HA support.  But closer to my own heart is the inclusion, finally, of websockets support.  I've blogged about it before, so I won't go in to too many details here.  But this is the first actual release of GlassFish to feature support for it which I'm personally pretty excited about. Some caveats still apply, however.  WebSockets are an exciting new technology but still remain under active development.  The version of the specification shipped with GlassFish 3.1 is the -76 version which is rather old by now.  It is still, as of this writing at least, the version supported by most of the browsers that support websockets.  So the good news is that apps you write today should work with most of the browsers out there.  As the specification evolves, however, browsers will begin to support newer versions of the specification and the GlassFish code will no longer work.

In the grizzly source tree, I have implemented support for the -05 version of the specification (with work started on the -06 version) for the 1.9 tree (which ships with GlassFish).  Should you find yourself needing newer support, I have written a simple bash script that will do that heavy lifting for you.  The script below needs to be run from the glassfish3/glassfish/modules directory.  In addition, you'll need to specify the version of grizzly you'd like to upgrade to.  The -05/-06 support will be present in 1.9.33 release coming up soon.

#! /bin/sh
NEWVER=$1

if [ -z "${NEWVER}" ]
then
	echo Please specify the new version as a parameter
	exit
fi

asadmin stop-domain
for i in grizzly-*
do
	DEP=${i/.jar/}
	wget http://download.java.net/maven/glassfish/com/sun/grizzly/${DEP}/${NEWVER}/${DEP}-${NEWVER}.jar
	[ $? -eq 0 ] && mv -v ${DEP}-${NEWVER}.jar $i
done

rm -r ../domains/domain1/osgi-cache/felix/

asadmin start-domain

This script will download the artifacts we've pushed to maven and update your existing grizzly jars.  It will also stop and restart your glassfish server for you so you don't need to worry about that.  The -05 spec is not compatible with the -76 version shipped with GlassFish 3.1 and we do not support the -76 version in the newer code for a variety of security/sanity related reasons.  So know that once you upgrade, you'll need browser support for the new version or your apps will stop working.

A word of warning, though.  I've done this locally and it works just fine.  But, as with any patch, you should back up your GlassFish directory first and/or test it in a development environment first.  If you have a support contract with Oracle, you'll likely need to discuss with them first to see how it would impact any agreements you have.  Ominous as that may sound, this should be safe enough to do (especially for a development environment) but just consider yourself warned.

WebSocket support is evolving constantly along the protocol itself.  While API changes are kept to a minimum, as we upgrade our support for the newer specifications and you upgrade your system to support them, you might encounter various changes in the API.  I really try to minimize such changes but please understand that things can change from release to release as everything beings to stabilize over the next few months.

I'm working on a number demos to help you get started with your own applications.  I'll be sharing a handful of those and presenting on building websocket applications at next month's The ServerSide Java Symposium in Las Vegas.  I'll be covering development using both 1.9 (and GlassFish 3.1) as well as grizzly 2.0 which just went final as well.  If you're there, please stop by and say hi.  I'd be happy to try to answer any questions you might have.

Grizzly 2.0 and Comet

With the release of Grizzly 2.0, I'd like to highlight one of its newest features: comet support. When implementing support for comet in grizzly 2.0, I tried as best I could to make it API compatible with your 1.9 applications. That said, there are a handful of changes that were made to clean things up a little and simplify some of the implementation details.  I'll outline the changes you'll need to make to existing apps before building a new one from scratch. Most of the changes are largely cosmetic. The first change you'll notice is that the package names have changed. With the advent of 2.0, grizzly now lives under the org.glassfish.grizzly rather than com.sun.grizzly. CometHandler is the core of any comet application code and it, too, has received a minor makeover:

  • attach() has been removed from the interface. This won't actually affect your code unless you happen to have used @Override. This method has been typically used to attach things like the HttpRequest object to your CometHandler instance. In reality, this kind of information can be passed via the constructor or a method on your subclass. Grizzly doesn't actually reference this method or the attachment in any way so it has been removed from the interface. You are, of course, welcome to continue using attach() if you'd like. But since Grizzly never uses it internally, there's no sense in forcing all implementations to implement it.
  • Four new methods have been added added to the interface:
    1. Response getResponse();
    2. void setResponse(Response response);
    3. CometContext<E> getCometContext();
    4. void setCometContext(CometContext<E> context);

    Your CometHandler implementation needs only provide these getters/setters and the fields they imply. Grizzly will handle the setting of those values itself. It didn't make it into 2.0 (because I got sidetracked) but 2.0.1 will have a DefaultCometHandler that takes care of that for you if you choose. The type of <E> should be consistent with the data type you want to pass to your handlers when an event occurs. Typically, this will simply be a String but, of course, could be almost anything.

  • The type of comet events have been modernized. In 1.9 you had a set of int constants to manage. In 2.0, I've changed them to an enum. This probably won't affect your code overly much but you do need to change any thing like CometEvent.READ to CometEvent.Type.READ. Enum comparisons being what they are, everything else should just compile as long as you used the named constants.
  • A great number of items have been deprecated as well. The compiler will highlight these for you. All the deprecated methods should continue to work as you would expect, but I would recommend changing over when you get the chance. (You're already having to tweak code anyway.) We've striven to keep things API compatible as much as possible to ease the transition, but these methods might go away in the future so it's best to be prepared.

    The riskiest change, to my mind, is that the execution type has been removed. The execution model of grizzly 2.0 makes supporting that feature complicated. However, when examining every comet app and unit test I could find, I didn't find that this feature was really ever used that much anyway. Indeed, when asked about it some of the original authors weren't really entirely sure about the feature anyway. If you find that you really need that feature the decision can be readdressed. But I have to warn you, prepare yourself for some disappointment because it seems unlikely to happen. But you never know.

That should cover the changes you'll see. There are a number of other changes behind the scenes, of course, but hopefully we've done a good job of shielding you from those. Now that we've seen what changes you need to make to an existing application, let's see what it takes to write a new one. With 2.0, creating a grizzly instance to run your apps becomes much, much simpler. From here on out, we'll take a look at the comet click counter example you can find in the samples folder of the source repository.

To start grizzly, you just need a few lines of code:

        HttpServer httpServer = HttpServer.createSimpleServer("./", PORT);
        httpServer.getServerConfiguration().addHttpHandler(new ServletHandler(new LongPollingServlet()),
            QUERY_PATH);
        final Collection listeners = httpServer.getListeners();
        for (NetworkListener listener : listeners) {
            listener.registerAddOn(new CometAddOn());
        }
        httpServer.start();

This snippet sets up all the HTTP bits you'll need to serve up your comet application. With that set up, the next piece is the servlet manage your requests and the handler that provides your application logic.

public class LongPollingServlet extends HttpServlet {
    final AtomicInteger counter = new AtomicInteger();
    private static final long serialVersionUID = 1L;
    private String contextPath = null;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        ServletContext context = config.getServletContext();
        contextPath = context.getContextPath() + "/long_polling";
        CometEngine engine = CometEngine.getEngine();
        CometContext cometContext = engine.register(contextPath);
        cometContext.setExpirationDelay(5 * 30 * 1000);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        CometEngine engine = CometEngine.getEngine();
        CometContext context = engine.getCometContext(contextPath);
        final int hash = context.addCometHandler(new CounterHandler(res, counter));
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
        counter.incrementAndGet();
        CometContext context = CometEngine.getEngine().getCometContext(contextPath);
        context.notify(null);

        PrintWriter writer = res.getWriter();
        writer.write("success");
        writer.flush();
    }
}
public class CounterHandler extends DefaultCometHandler {
    private HttpServletResponse httpResponse;
    private AtomicInteger counter;

    CounterHandler(HttpServletResponse httpResponse, final AtomicInteger counter) {
        this.httpResponse = httpResponse;
        this.counter = counter;
    }

    public void onEvent(CometEvent event) throws IOException {
        if (CometEvent.Type.NOTIFY == event.getType()) {
            httpResponse.addHeader("X-JSON", "{\"counter\":" + counter.get() + " }");
            PrintWriter writer = httpResponse.getWriter();
            writer.write("success");
            writer.flush();
            event.getCometContext().resumeCometHandler(this);
        }
    }

    public void onInterrupt(CometEvent event) throws IOException {
        httpResponse.addHeader("X-JSON", "{\"counter\":" + counter.get() + " }");
       PrintWriter writer = httpResponse.getWriter();
        writer.write("success");
        writer.flush();
    }
}

The init() in the servlet registers the comet context we'll use to track all the handlers that various requests will park. That's the java code you need. Without overdoing it, a quick look at the javascript side should cover it.

var counter = {
      'poll' : function() {
         new Ajax.Request('long_polling', {
            method : 'GET',
            onSuccess : counter.update
         });
      },
      'increment' : function() {
         new Ajax.Request('long_polling', {
            method : 'POST'
         });
      },
      'update' : function(req, json) {
         $('count').innerHTML = json.counter;
         counter.poll();
      }
}

When the page loads, it makes the initial GET request to the server. That request gets parked in doGet() above. That request will stay open until the user clicks on a link in the html from the sample application. That click triggers a POST post. In doPost(), we call notify() on the context which triggers the parked CometHandler to finally respond to that initial GET request. Once that request finally responds, the javascript code will make another GET which gets parked and the process repeats itself. In this way multiple clients can track and update the counter on the server.

This is just a basic example, of course, ut it covers all the basic building blocks you'll need for more complex applications. For more examples, you can look through the sample applications. There are more examples in 1.9 than 2.0 right but, changes listed above aside, should help you find the answers. We're putting more and more documentation up at the website and, of course, we have mailing lists where you can post your questions. I think you'll find that 2.0 is much more pleasant to work with. So please take a look and give us some feedback on the lists. We'd love to hear about your experiences.

Grizzly/Kenai update

Well, it took longer than we'd hoped but we're finally live on the kenai infrastructure.  Grizzly is one of a handful of pilot migrations trying to work the kinks out so we may still have a few issues here and there but I think the worst is behind us.  I'd like to run down the big changes at least to help you find your way in the new set up.

Issue Tracking

The first big change that I'm personally very excited about is the issue tracker.  We're now using Jira instead of the ancient bugzilla instance we've been using.  All the issues have been imported and none of the bug IDs should have changed.  The URLs are all different of course.  You can find the new tracker at http://java.net/jira/browse/GRIZZLY and it's also listed on the new home page location of http://grizzly.java.net.  (All projects on the new kenai system have basically moved from *.dev.java.net to *.java.net.)  Jira gives a us a clearer view of what's remaining and where each line of development stands.  I've cleaned up the tracker where I thought it needed it so it should be much easier to track your favorite issues now.

Subversion

Subversion has also moved for those of you who have checked out the repository.  The new URL is https://svn.java.net/svn/grizzly~svn so each line of development can now be found at:

  1. 1.9.x ->  https://svn.java.net/svn/grizzly~svn/trunk/code
  2. 2.x -> https://svn.java.net/svn/grizzly~svn/branches/2dot0
  3. 1.0.x -> https://svn.java.net/svn/grizzly~svn/branches/grizzly1.0

To switch your local copy to the new url simply this from the root of your local workspace (using trunk as an example)

svn switch --relocate https://grizzly.dev.java.net/svn/grizzly/trunk/code https://svn.java.net/svn/grizzly~svn/trunk/code

Your password may or may not need to be reset.  Sadly, this is one area where we've hit a number of bumps.  Resetting is easy enough from the website just be aware that you might need to do this.  If you have commit access at least.  This is the same login you'd use for the jira so you might need to do this even as an observer.

Mailing Lists

The addresses have changed for the mailings.  For dev, it's dev@grizzly.java.net.  For users, it's users@grizzly.java.net.  The old addresses forward to the new ones so if you send to the old ones, they'll continue to find their way to the list but I'm not sure how long those forwards will be around.  It would be wise to take a second to update your address books just to be safe.

Web Site

Finally, as I mentioned, the website has a new location:  http://grizzly.java.net.  The old URL will forward so any bookmarks should be fine for a while.  But again, there's been no indication of how long such forwarding will be in place so it wouldn't hurt to update your links.  It should look pretty familiar as we've tried to retain the old look and feel but it is slightly different.  Before anyone complains/comments, I know some of the source/javadoc links on the left don't work yet.  We're still working on realigning to the new infrastructure and we'll have to clean those up as we go.  We'll also be reworking the content on the landing page since 2.0 figures to loom larger in the coming year.

That's about it.  Take some time to explore the new system.  Let us know if you run into any issues.  Rather than posting to the mailling list, it'd help us track things if you'd file issues against the www component in jira:  http://java.net/jira/browse/GRIZZLY/component/10009.  I think you'll find the new system much more pleasurable to work with, though.  Cheers.