Antwerkz, Inc.
30Sep/11Off

So Long and Thanks for All the GlassFish

Posted by admin

I'm both excited and slightly saddened to announce that I'm leaving Oracle.  I've enjoyed my time here, the work I've done, and with whom I've done it.  Sometimes an opportunity comes up that's too enticing to pass up, though, and you just have to take it.  I hope I can continue to help the evolution of websockets in EE and plan on applying to join that EG as soon as it forms but we'll see.  I hope to with the Grizzly implementation, as well, as the standards start to evolve but again we'll have to see how things play out.

It's a bittersweet thing but I'm excited about it overall.  I have my JavaOne duties next week and then one more week with Oracle and then it's on to next thing.  If you're at JavaOne next week, stop by my session and say hi.  Now to finish packing...

Technorati Tags:

Tagged as: 1 Comment
30Aug/10Off

Port Unification in GlassFish 3 – Part 4

Posted by admin

This entry is part 4 of 4 in the series Port Unification in GlassFish

In what should be the last entry in this series, we'll cover a late addition to port unification.  As I mentioned earlier, GlassFish 3.1 isn't adding support for port unification, we're simply introducing commands to make it accessible to people beside GlassFish hackers.  As often happens, though, once these commands were revealed to the wider community, one of the first questions was "How can I redirect to a different port?"  The short answer used to be "you can't."  But thanks to Ryan Lubke, now you can and we'll see how below.

The steps are pretty similar to what we've seen so far.  There are 4 different use cases covered here.  HTTP->HTTPS (and vice versa), and redirecting on the same or a different port.  In the steps listed below, I've tried to reuse existing elements of the config as much as possible to avoid polluting the configuration file with too much cruft.  However, in some cases it was simpler to just create new structures because of some validation checks asadmin performs to try to prevent invalid configurations from cratering your instance.

###
### HttpToHttpsRedirectOnSamePort
###
asadmin create-protocol --securityenabled=false http-redirect
asadmin create-http-redirect --secure-redirect true http-redirect
asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-listener-2 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-1.protocol=pu-protocol
 
###
### HttpToHttpsRedirectOnDifferentPort
###
asadmin create-protocol --securityenabled=false http-redirect
asadmin create-http-redirect --redirect-port 8181 --secure-redirect true http-redirect
asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-listener-2 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-1.protocol=pu-protocol
 
###
### HttpsToHttpRedirectOnSamePort
###
asadmin create-protocol --securityenabled=true http-redirect
asadmin create-http-redirect --secure-redirect false http-redirect
asadmin create-ssl --certname s1as --type network-listener --ssl2enabled=false --ssl3enabled=false --tlsenabled=true --tlsrollbackenabled=true --clientauthenabled=false http-redirect
asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-listener-1 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.protocol=pu-protocol
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.enabled=true
 
###
### HttpsToHttpRedirectOnDifferentPort
###
asadmin create-protocol --securityenabled=true http-redirect
asadmin create-http-redirect --redirect-port 8080 --secure-redirect false http-redirect
asadmin create-ssl --certname s1as --type network-listener --ssl2enabled=false --ssl3enabled=false --tlsenabled=true --tlsrollbackenabled=true --clientauthenabled=false http-redirect
asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-listener-1 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --targetprotocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.protocol=pu-protocol
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.enabled=true

A few of the options on the create-ssl commands you could probably remove if you'd like. I added them simply to make the created structures match the default ones to reduce any confusion and clutter while looking at the resulting domain.xml.

Pick the scenario you need and cut and paste the relevant part of the above script into your terminal window and let glassfish do the rest. You can verify your set up by using wget -S to request index.html at the end of whichever url you need and look at the header responses to make sure you're properly getting the relocation information. For example, if you're doing https -> http redirection on different ports, you'd do (and see) this:

wget -q -S --no-check-certificate https://localhost:8181
 
  HTTP/1.1 302 Moved Temporarily
  Location: http://localhost:8080/
  Connection:close
  Cache-control: private
  HTTP/1.1 200 OK
  Content-Type: text/html
  Content-Length: 5212
  Date: Mon, 30 Aug 2010 16:35:24 GMT
  Connection: Keep-Alive

There you go. Redirecting HTTPS on 8181 to HTTP on 8080. It's that easy. At this point I feel I should point out that redirect from secure to nonsecure connections is almost always a monumentally bad idea but I'm sure there are a handful of uses for it. Regardless, these 4 use cases should cover anything you might need. These steps require a nightly build of GlassFish 3.1 but if you're already using/experimenting with those builds, please try it out. Let us know if you have any issues on the users list and we'd be happy to help.

Technorati Tags: ,

11Aug/10Off

Fall Conference Update

Posted by admin

I've tweeted about it now and then but wanted to give a complete picture of what's happening this fall.  It seems like fall is tech conference season doesn't it?  Well, let's start with the Big One:  JavaOne.  I submitted three different proposals and got two accepted (though one as an alternate).  Everything looked in order but life is fraught with random changes and disappointments.  Despite my initial approval from management, my trip to JavaOne has been cancelled due to ... budgetary restraints.  I was schedule to present session S314395: "WebSockets Versus Comet: What Are the Differences, and Why Should I Care?" with Oleksiy Stashok.  I won't be doing that now, of course, but Oleksiy will still be presenting it by himself, though.  It's a shame I can't be there.  There's a lot of websocket action this year at JavaOne and I would've loved to go to some of the other sessions.

That's a big disappointment.  However, I will still be presenting at JavaZone in Oslo.  I'll presenting a session called "Building Websocket Based Applications on GlassFish/Grizzly."  That'll be on the afternoon of the second day.  If the demo gods are kind, I'll be showing off at least one interesting demo.  Maybe two if I can get another one pulled together in time.  Since I won't be preparing for as much conference action as I'd thought I'd be hopefully, that won't be a problem.  ;)

26Jul/10Off

Port Unification in GlassFish 3 – Part 3

Posted by admin

This entry is part 3 of 4 in the series Port Unification in GlassFish

With this third installment, we're getting to the most interesting feature:  serving multiple protocols on one port.  The uses of this are fairly obvious:  simpler administration, easier on firewalls, etc.  Many of the elements we've already seen and so should be fairly familiar.  We'll simply build on things we've seen and take them just one step further.  In this simple demo, we'll be serving up HTTP traffic and a "dummy" protocol.  We'll look at at the code needed to implement this protocol once we cover how to configure it.

The first step we need to is to copy the attached protocol zip file in to GlassFish so it's ready for us to use.  Once you've downloaded the zip file, copy it to <glassfish>/domains/domain1/autodeploy/bundles/.  (You might need to create this directory first.)  If you're watching the server logs, you'll notice in a few seconds that GlassFish has detected the file and has deployed it.  With that done, we can configure the system to serve up that protocol.

This script is very similar to what we've already seen:

asadmin create-protocol pu-protocol
 
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-listener-1 --classname com.sun.grizzly.http.portunif.HttpProtocolFinder http-finder
 
asadmin create-protocol pu-dummy-protocol
asadmin create-protocol-finder --protocol pu-protocol --target-protocol pu-dummy-protocol --classname org.glassfish.devtests.web.portunif.DummyProtocolFinder dummy-finder
asadmin create-protocol-filter --protocol pu-dummy-protocol --classname org.glassfish.devtests.web.portunif.DummyProtocolFilter dummy-filter
 
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-1.protocol=pu-protocol

As you can see the steps here are almost identical to what we've done in the past. In this case, we're reusing the http-listener-1 <protocol> definition and pointing the HttpProtocolFinder there. This is the default protocol definition that ships with GlassFish. We're creating a new protocol definition for the dummy protocol and creating the finder and filter for the protocol. As a last step, we update the http-listener-1 <network-listener> to use the new pu-protocol we defined. And with that we're ready to test.

The HTTP test is simple enough: point your browser and http://localhost:8080. You should seen the standard GlassFish welcome (or an app if you've deployed one to that location). Testing the dummy protocol is a little more involved but not too bad. The protocol is simple enouugh. It looks for the text dummy-protocol and then responds with Dummy-Protocol-Response. If you have the nc command installed, you can simply do this:

echo dummy-protocol | nc localhost 8080

If not, you can you the telnet command. Simply telnet into localhost port 8080 and and the prompt enter dummy-protocol and hit the enter key. The server will respond with Dummy-Protocol-Response. Then you know you're done. We've seen this kind of configuration several times before. Let's look at the code that drives.

The first player in all this is the finder. This code detects the dummy protocol and responds accordingly. The finder looks like this:

public class DummyProtocolFinder implements ProtocolFinder {
    private final static String name = "dummy-protocol";
    private byte[] signature = name.getBytes();
 
    public String find(Context context, PUProtocolRequest protocolRequest)
            throws IOException {
        ByteBuffer buffer = protocolRequest.getByteBuffer();
        int position = buffer.position();
        int limit = buffer.limit();
        try {
            buffer.flip();
            if (buffer.remaining() &gt;= signature.length) {
                for(int i=0; i

This code simply scans the incoming bytes looking for "dummy-protocol." if it can't find it, it returns null telling the underlying grizzly code to keep looking. If it finds it, returns that string to the caller signifying it's found the protocol. At that point, control gets handed off to the DummyProtocolFilter:

public class DummyProtocolFilter implements ProtocolFilter {
    public boolean execute(Context ctx) throws IOException {
        SelectableChannel channel = ctx.getSelectionKey().channel();
        OutputWriter.flushChannel(channel, ByteBuffer.wrap("Dummy-Protocol-Response".getBytes()));
        ctx.getSelectorHandler().closeChannel(channel);
        return false;
    }
 
    public boolean postExecute(Context ctx) throws IOException {
        return true;
    }
}

The filter is simple enough. It just prints back the text we've been expecting. When it's done it returns false to show that execution is finished and the response can be closed out. Obviously a more complex protocol would require a more complex filter, but this is the basis of any such filter.

That's all it takes. If you wanted to serve up more protocols, you'd simply add more filters and finders as needed.

Technorati Tags: ,

23Jul/10Off

Port Unification in GlassFish 3 – Part 2

Posted by admin

This entry is part 2 of 4 in the series Port Unification in GlassFish

It's taken more time to get back to this topic but it's time.  In part 1, I covered how to set up GlassFish to push all HTTP traffic to HTTPS.  In this post, I'll show you how to set up the reverse.  In the next post, I'll cover how to configure GlassFish to serve up multiple protocols from the same port.  The steps are basically the same so this will be a short one.  Similar to last time, we'll issue a few simple commands:

asadmin create-protocol --securityenabled=true https-redirect
asadmin create-protocol-filter --protocol https-redirect --classname com.sun.grizzly.config.HttpRedirectFilter redirect-filter
asadmin create-ssl --certname s1as --type network-listener --ssl2enabled false --ssl3enabled false --clientauthenabled false https-redirect
 
asadmin create-protocol pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-listener-1 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --target-protocol https-redirect --classname com.sun.grizzly.config.HttpProtocolFinder https-redirect
 
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.protocol=pu-protocol
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-2.enabled=true

This should familiar if you've read part 1. We do a little extra work to set up some ssl config elements primarily to preserve the standard settings in case you want to roll back these changes when you're done. If you do, you simply need to delete those new protocol elements.

To see it in action, simply issue the following command:

wget -q -S --no-check-certificate https://localhost:8181/

You should see something like the following:

HTTP/1.1 302 Moved Temporarily
Location: http://localhost:8181/
Connection:close
Cache-control: private
HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1-SNAPSHOT Java/Apple Inc./1.6)
Server: GlassFish Server Open Source Edition 3.1-SNAPSHOT
Accept-Ranges: bytes
ETag: W/"5212-1279828070000"
Last-Modified: Thu, 22 Jul 2010 19:47:50 GMT
Content-Type: text/html
Content-Length: 5212
Date: Fri, 23 Jul 2010 16:50:22 GMT
Connection: Keep-Alive

As you can see, the https request received an initial 302 response pushing off to the http url which then returns the 200 response we'd expect. To verify even further, use wget to fetch the http url and you'll see something like this:

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1-SNAPSHOT Java/Apple Inc./1.6)
Server: GlassFish Server Open Source Edition 3.1-SNAPSHOT
Accept-Ranges: bytes
ETag: W/"5212-1279828070000"
Last-Modified: Thu, 22 Jul 2010 19:47:50 GMT
Content-Type: text/html
Content-Length: 5212
Date: Fri, 23 Jul 2010 16:57:31 GMT
Connection: Keep-Alive

That's it. As promised, short and sweet. Currently this approach does not allow you redirect to a different port number. We have added a new configuration element that simplifies this setup and allows for cross-port redirects. However since there isn't asadmin support for it yet, I'll defer discussion until we can get those commands written. That's in the works so it should be in the next week or two.

With that, though, I'll wrap this one up and start working on the more interesting Part 3.

Technorati Tags: ,

7Jun/10Off

Port Unification in GlassFish 3 – Part 1

Posted by admin

This entry is part 1 of 4 in the series Port Unification in GlassFish

There are two main cases I want to cover here:  port redirection and serving multiple protocols on one port.  I'm pretty sure that covering both in one post would egregiously long so I'm going to break things up into two posts.  In this first post, we'll cover what will likely the be more common use case:  port redirection.  In this example scenario, you only want to serve requests via https rather than http.  In this case, you can configure GlassFish to return a 302 and push clients off to https transparently.  This type of configuration has been available in GlassFish since at least v2 and is still available in v3.  However, in v3 it wasn't "officially" supported so you had to manually massage your domain.xml.

While documented in various blogs around net, it's not officially supported in v3.  As of, well now I guess, that's all changed.  I've added some asadmin commands to the 3.1 nightly builds that expose a more user friendly way to configure port unification.  In this blog, I'll introduce these commands and show you how to set up a simple redirect to enforce https requests on a given listener.  As always, when experimenting with this sort of thing, you should back up your domain.xml so if you hose your configuration, you can easily roll back to a working configuration.

There are two main concepts involved:  <protocol-filter> and <protocol-finder>.  There are a handful of other elements involved but we've hidden them from you by default since very few people will need to manage those elements.  The first step is to create the <protocol-filter>:

asadmin create-protocol --securityenabled=false http-redirect
asadmin create-protocol-filter --protocol http-redirect --classname com.sun.grizzly.config.HttpRedirectFilter redirect-filter

We need to create a new <protocol> element to hold our new filter first.  You can't simply reuse, say, http-listener-1 because port unification elements and <http>/<ssl> can't coexist on the same protocol elements.  You could of course issue a number of commands to preserve the name of http-listener-1 but the work involved is likely not worth it.  It's certainly overkill for this example so I'll leave that as an exercise for you if you're that interested in it.  The next asadmin command creates the actual protocol filter.  With these two commands we have now have a new entry in our domain.xml that looks like this:

<protocol name="http-redirect">
   <protocol-chain-instance-handler>
      <protocol-chain>
         <protocol-filter classname="com.sun.grizzly.config.HttpRedirectFilter" name="redirect-filter" />
      </protocol-chain>
   </protocol-chain-instance-handler>
</protocol>

Notice that there are new elements: <protocol-chain-instance-handler> and <protocol-chain>. These are some implementation details that should be mostly irrelevant to all but a handful of users. I only mention them so that you'll be expecting to see them and that you know you can safely skip over them. With those pieces in place, we can now create our finders.

To create our finders, we need to create another <protocol> element to hold them and then create the finders themselves:

asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-listener-2 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect

Note that the first <protocol-finder> refers to the http-listener-2 <protocol>. We'll be using that protocol definition to configure the https processing. The second finder refers to the <http-finder protocol> definition we just created and it is this piece that will be doing the redirect from http->https for us. The first finder will trap all https requests and hand them off, while the second will handle all cleartext http requests and redirect for us. The resulting domain.xml elements looks like this:

<protocol name="pu-protocol">
   <port-unification>
      <protocol-finder protocol="http-listener-2" classname="com.sun.grizzly.config.HttpProtocolFinder" name="http-finder" />
      <protocol-finder protocol="http-redirect" classname="com.sun.grizzly.config.HttpProtocolFinder" name="http-redirect" />
   </port-unification>
</protocol>

Again, we silently create the <port-unification> element for you so you needn't worry about it. These elements that we silently create for you, we will also silently delete them for you when you delete the last elements contained in them. So those are our port unification elements. With those created, we just need to reconfigure listener to use these new elements:

asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-1.protocol=pu-protocol

With that, we can try hitting port 8080 and see things in action. The easiest way to see this, probably, is to use wget:

wget -q -S http://localhost:8080/

With this command, you should see the following output:

  HTTP/1.1 302 Moved Temporarily
  Location: https://localhost:8080/
  Connection:close
  Cache-control: private

As you can see there, the server returns a 302 back to the client with the new location of https://localhost:8080. That's all it takes. Now every request will use https regardless of the original request. You could, of course, use a similar set up to push everyone from https back to http. This is especially useful for those without any need for https and are concerned about server load since https can be expensive to process.

In the next entry, I'll tackle the use case of serving up multiple protocols from one listener. This will be especially useful for those behind firewalls wanting to open only a single port to the outside world. Hopefully this will get you started. If you have any questions, feel free to leave a comment or ask on the glassfish users mailing list.

For convenience, here are all the commands necessary to try this out at home in one downloadable file:

wget -q -S http://localhost:8080/
 
asadmin create-protocol --securityenabled=false http-redirect
asadmin create-protocol-filter --protocol http-redirect --classname com.sun.grizzly.config.HttpRedirectFilter redirect-filter
 
asadmin create-protocol --securityenabled=false pu-protocol
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-listener-2 --classname com.sun.grizzly.config.HttpProtocolFinder http-finder
asadmin create-protocol-finder --protocol pu-protocol --target-protocol http-redirect --classname com.sun.grizzly.config.HttpProtocolFinder http-redirect
 
asadmin set configs.config.server-config.network-config.network-listeners.network-listener.http-listener-1.protocol=pu-protocol
 
wget -q -S http://localhost:8080/

Technorati Tags: ,

14May/10Off

Scripts to Help with GlassFish Development

Posted by admin

GlassFish is a big enough source base that sometimes you just need a little help managing the development lifecycle. Over time I've developed a number of scripts that I use while working on either grizzly or glassfish to help manage the load. After a number of discussions, I've decided to share them in the hopes they will help others, too. Not of all these scripts are really glassfish or grizzly related so you might find them useful in your own projects as well. You can check out these scripts using git from this url: git://kenai.com/schema-doc~git-repo.

There are several scripts but I'll try highlight the more interesting ones. Just a note, though. These are bash scripts that have grown organically for a long time. So they're not necessarily going to be pretty. Some might even consider some of the hoops I've jumped through "stupid." That's fine. I'm not getting a Ph.D. with these. They work and that's enough for me. But anyway.

General Scripts

The first set of scripts that should apply to almost any project.

script description
changed.sh
unknown.sh
These scripts will show you any changed (or unknown) files for whichever VCS you're using. They currently support subversion, mercurial, and git. The git support is new-ish so let me know if something's off
findInJar.sh As the name implies, this script will find every jar in or under the current director and grep for, well, really whatever you tell it. I wrote it with looking for classes in mind but since it just greps the contents, it will find anything that matches. It seems like everyone eventually writes a similar script so maybe this will save some people a little time.
failedTests.sh This script requires that you use maven. It will run mvn surefire-report:report-only and scan for any failed tests. If it finds any, it will use the open command to open the report html in your default browser. It can, optionally, run your tests before looking for failures. If you'd like it to do this, simply pass --run to the script.

GlassFish/Grizzly Related Scripts

Obviously, these scripts will be of little interest to those not working with some aspect of glassfish development. But if you're not, you're probably reading the wrong blog entry anyway. All of these scripts rely on the presence of environment variables. The scripts are set up to check for the variables and prompt you to define them so I won't go into them here. Just be aware that at first you'll have to define a few variable before these scripts will work for you. And these scripts need a UNIXy environment so if you're on Windows, you'll need something like cygwin to make these work. But even then, I've not tried these with cygwin so you might have issues even still. I'll refer to some of these variables by name below, but the script will walk you through what to set to what.

script description
distro.sh This script will build the glassfish distribution bundles. Most of the heavy lifting is really done by maven but this script goes a step further and extracts the "glassfish" distribution of into ${GF_INSTALL}. Executed without parameters, it will build the distro, remove the current install, and unzip the new one. There are 3 options you can pass to this one:

  1. --nobuild: only extract the bundle zip.
  2. --buildonly: just build the bundles. don't extract them.
  3. --clean: have maven clean out the compiled artifacts before building the distributions
devtests.sh This one is really specific. This will run the webtier devtests after reconfiguring the glassfish install in ${GF_INSTALL}. It can be run from anywhere so you don't need to worry about where to launch this one. Once the devtests finish, it will open the test_results.html displaying the results of the tests. This one takes a while to run. There are a lot of tests...
single.sh Similar to devtests.sh, this test will run a single test in the webtier devtests. Just pass it the name of the directory while in v2/appserv-tests/devtests/web and it'll do the rest.
quicklook.sh This script runs the quicklook tests to test your v3 tree. This script takes --debug and will run the quicklook tests using the mvnDebug script.
updateBundle.sh This script compiles the current maven module and copies the resultant jar into your glassfish install. This should work for any glassfish-related project. I use it with grizzly, too, for example.
rebundle.sh This script will scan svn looking for changed files and try to determine the module owning each file. It will then call updateBundle for each of those modules and update your glassfish install. Passing --start will then launch glassfish with your updated code.
startgf.sh
stopgf.sh
These scripts will start/stop glassfish from wherever you may be in the filesystem. Passing --debug will launch glassfish in a debug VM. It will also update your domain.xml such that the launch will pause until you connect to it with a debugger so be aware that things will appear to hang until you do.
tailgf.sh This will tail your glassfish's server.log from wherever you are in the filesystem. If glassfish is not yet running, it will also truncate the logs first.

That about does it. I use these scripts daily and I find them quite useful. Hopefully, you do as well.

Technorati Tags: , ,

8Apr/10Off

GlassFish Web Sockets Sample

Posted by admin

A few weeks back I blogged about the impending GlassFish support for the emerging Web Sockets standard. To update, I have some good news and some some ok news. The good news is that the implementation has stabilized enough to show some concrete code. The ok news is that, despite my best efforts and overly persistent petitioning, this support won't show up in 3.0.1. You'll need to use the GlassFish nightly builds to play with it. Hopefully that's OK given that web sockets are still evolving anyway and browser support is spotty at best. With that out of the way, let's look at a simple example.

For familiarity's sake, and for simple comparison, I ported the comet chat sample in grizzly to use web sockets. I wanted to see how different the code would look between two similar applications. So if you're familiar with the comet example, the web socket example should be very familiar. At the heart of grizzly's/glassfish's web socket support is the WebSocketApplication. For our chat, it's pretty simple: every time someone sends a message, broadcast it to everyone. The code is pretty simple:

package com.sun.grizzly.samples.websockets;
 
import com.sun.grizzly.tcp.Request;
import com.sun.grizzly.tcp.Response;
import com.sun.grizzly.websockets.DataFrame;
import com.sun.grizzly.websockets.WebSocket;
import com.sun.grizzly.websockets.WebSocketApplication;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
 
public class ChatApplication extends WebSocketApplication {
    List<webSocket> sockets = new ArrayList<webSocket>();
 
    @Override
    public WebSocket createSocket(Request request, Response response) throws IOException {
        final ChatWebSocket socket = new ChatWebSocket(this, request, response);
 
        sockets.add(socket);
        return socket;
    }
 
    public void onMessage(WebSocket socket, DataFrame frame) {
        final String data = frame.getTextPayload();
        if (data.startsWith("login:")) {
            login((ChatWebSocket) socket, frame);
        } else {
            broadcast(data);
        }
    }
 
    public void onConnect(WebSocket socket) {
    }
 
    private void broadcast(String text) {
        WebSocketsServlet.logger.info("Broadcasting : " + text);
        for (WebSocket webSocket : sockets) {
            send(webSocket, text);
        }
 
    }
 
    private void send(WebSocket socket, String text) {
        try {
            socket.send(text);
        } catch (IOException e) {
            WebSocketsServlet.logger.log(Level.SEVERE, "Removing chat client: " + e.getMessage(), e);
            onClose(socket);
        }
    }
 
    public void onClose(WebSocket socket) {
        sockets.remove(socket);
    }
 
    private void login(ChatWebSocket socket, DataFrame frame) {
        if (socket.getUser() == null) {
            WebSocketsServlet.logger.info("ChatApplication.login");
            socket.setUser(frame.getTextPayload().split(":")[1].trim());
            broadcast(socket.getUser() + " has joined the chat.");
        }
    }
}

As you can see, we implement some very basic logic here. This, of course, could be as complex as you need it it to be. In a real chat application, for example, we'd have rooms/channels and the like but we're keeping it simple here. The other class to note is ChatWebSocket. There's not much need to provide custom WebSocket implementations but in this case, I chose to store the chat user's user name on the WebSocket. I could've used a Map in ChatApplication and used the default implementation but this seemed a bit more ... OOPish. The implementation there is, again, quite simple:

package com.sun.grizzly.samples.websockets;
 
import com.sun.grizzly.tcp.Request;
import com.sun.grizzly.tcp.Response;
import com.sun.grizzly.websockets.BaseServerWebSocket;
 
import java.io.IOException;
 
public class ChatWebSocket extends BaseServerWebSocket {
    private String user;
    private final ChatApplication app;
 
    public ChatWebSocket(final ChatApplication listener, Request request, Response response) {
        super(listener, request, response);
        app = listener;
    }
 
    public String getUser() {
        return user;
    }
 
    public void setUser(String user) {
        this.user = user;
    }
 
    @Override
    public void send(String data) {
        super.send( toJsonp(getUser(), data) );
    }
 
    @Override
    public void close() throws IOException {
        WebSocketsServlet.logger.info("closing : " + getUser());
        app.remove(this);
        super.close();
    }
 
    private String toJsonp(String name, String message) {
        return "window.parent.app.update({ name: \"" + escape(name) + "\", message: \"" + escape(message) + "\" });\n";
    }
 
    private String escape(String orig) {
        StringBuilder buffer = new StringBuilder(orig.length());
 
        for (int i = 0; i < orig.length(); i++) {
            char c = orig.charAt(i);
            switch (c) {
                case '\b':
                    buffer.append("\\b");
                    break;
                case '\f':
                    buffer.append("\\f");
                    break;
                case '\n':
                    buffer.append("<br />");
                    break;
                case '\r':
                    // ignore
                    break;
                case '\t':
                    buffer.append("\\t");
                    break;
                case '\'':
                    buffer.append("\\'");
                    break;
                case '\"':
                    buffer.append("\\\"");
                    break;
                case '\\':
                    buffer.append("\\\\");
                    break;
                case '<':
                    buffer.append("&lt;");
                    break;
                case '>':
                    buffer.append("&gt;");
                    break;
                case '&':
                    buffer.append("&amp;");
                    break;
                default:
                    buffer.append(c);
            }
        }
 
        return buffer.toString();
    }
}

Again, it's pretty simple. There's some extra code for escaping the text since in our javascript, we'll simply eval() the text and let the browser work its magic. With these two pieces in place, we're almost done on the server. In order to let the web sockets system know of our application, we need to register it. (This is an optional step that we'll cover later.) In our case, we have a servlet in our web app. The servlet actually doesn't do anything. It doesn't override doGet() or doPost(). It only really exists for this one method:

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

This line registers the request URI with the WebSocketEngine so that it knows that when it sees a web socket request come in that matches that URI, to hand things off to the appropriate application. For our chat sample, we need an application to handle the logic related to running a chat system. However, if you don't need such logic, you don't even have to register (or write) and application class at all.

When a web socket request comes in, grizzly will look for any registered application for that URI. If it can be found, handling is handed off and grizzly's request handling is done. However, if no application can be found, grizzly can still process the web socket request by deferring the request to whatever the ultimate endpoint is. Grizzly will still frame anything written back to the client from, say, the servlet such that your web socket client gets a proper response. This scenario doesn't support bidirectional conversations, but it does allow you to expose any URL to a web socket client with no additional work.

There is one final step before we can try out the sample. Web socket support is disabled by default in grizzly and in glassfish so we must enable it first. In glassfish, we can do this by issuing this command:

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

You will need to adjust the name of the protocol element to match your system if you changed any of that, but if you use the standard configuration, that's all you need. If you're using grizzly outside of glassfish, you have neither asadmin nor a domain.xml. For that, you'll need to enable web socket support on your SelectorThread:

        SelectorThread st = new SelectorThread();
 
        /*  whatever other settings you might need */
        st.setAsyncHandler(new DefaultAsyncHandler());
        st.setEnableAsyncExecution(true);
        st.getAsyncHandler().addAsyncFilter(new WebSocketAsyncFilter());

That does it for the server side. On the client side we have the usual mix of HTML and javascript. I don't want to go in to depth on most of that but I do want to take a look at one particular piece. In application.js, we have this code:

?View Code JAVASCRIPT
var app = {
    url: 'ws://localhost:8080/grizzly-websockets-chat/chat',
    initialize: function() {
        if ("WebSocket" in window) {
            $('login-name').focus();
            app.listen();
        } else {
            $('missing-sockets').style.display = 'inherit';
            $('login-name').style.display = 'none';
            $('login-button').style.display = 'none';
            $('display').style.display = 'none';
        }
    },
    listen: function() {
        $('websockets-frame').src = app.url + '?' + count;
        count ++;
    },
    login: function() {
        name = $F('login-name');
        if (! name.length > 0) {
            $('system-message').style.color = 'red';
            $('login-name').focus();
            return;
        }
        $('system-message').style.color = '#2d2b3d';
        $('system-message').innerHTML = name + ':';
 
        $('login-button').disabled = true;
        $('login-form').style.display = 'none';
        $('message-form').style.display = '';
 
        websocket = new WebSocket(app.url);
        websocket.onopen = function() {
            // Web Socket is connected. You can send data by send() method
            websocket.send('login:' + name);
        };
        websocket.onmessage = function (evt) {
            eval(evt.data);
            $('message').disabled = false;
            $('post-button').disabled = false;
            $('message').focus();
            $('message').value = '';
        };
        websocket.onclose = function() {
            var p = document.createElement('p');
            p.innerHTML = name + ': has left the chat';
 
            $('display').appendChild(p);
 
            new Fx.Scroll('display').down();
        };
    },
    post: function() {
        var message = $F('message');
        if (!message > 0) {
            return;
        }
        $('message').disabled = true;
        $('post-button').disabled = true;
 
        websocket.send(message);
    },
    update: function(data) {
        if (data) {
            var p = document.createElement('p');
            p.innerHTML = data.name + ': ' + data.message;
 
            $('display').appendChild(p);
 
            new Fx.Scroll('display').down();
        }
    }
};

Most of that is prototype/scriptaculous code for managing the UI but notice in login() how we connect to the server and define our callbacks for when we receive various events. In post() we send our entry to the server and in update() we take what the server gives us and update the UI accordingly. It's a very simple and naive system to be sure but serves to illustrate the basics without getting mired down in details of building a large scale, fault tolerant system. Yes, I know there are things that could be improved with this or that detail. But I'm not building a replace for Yahoo! chat. :) If you want to see the code in full, you can browse the grizzly subversion repository here.

Now, there *is* some bad news but it's temporary at least. This code is in the trunk of grizzly's subversion repository and will be included in the 1.9.19-beta2 release. This hasn't been integrated into glassfish 3.1 yet but will be soon. Until then, you'll need to build your own grizzly tree and glassfish distribution with the root pom updated to use 1.9.19-SNAPSHOT. This is, indeed, quite ugly but we should be integrating a new 1.9.19 beta soon and then you can simply use the nightly builds.

If you have any questions, please leave a comment or, better yet, join the dev and/or users mailing lists here. There's still some tweaking left to do here and there but things are shaping up quite nicely, I think. But I won't know what's missing or wrong unless you tell me. So, please, play. Comment. Let me know what you think.

Technorati Tags: , , ,

3Mar/10Off

Wicket and Embedded GlassFish

Posted by admin

One of the newer features available in GlassFish v3 is the ability to run glassfish in an embedded mode similar to how jetty is often used. This is approach is very common when working with Wicket and is, in fact, part of the quickstart maven archetype.  So what I'd like to see is GlassFish in place of Jetty.  It's not terribly difficult once you get past the first few steps.  This is a pretty new (and evolving) option so the documentation isn't all that easy to find, necessarily, but it's building.  I'll list some resources at the end.

The first step, of course, is to configure maven appropriately.  In my pom.xml, I have the following:

    <repositories>
        <repository>
            <id>glassfish-repository</id>
            <name>GlassFish Nexus Repository</name>
            <url>http://maven.glassfish.org/content/groups/glassfish</url>
        </repository>
    </repositories>

This will add the repository to find the deployed GlassFish artifacts. With that done, we can define a few dependencies:

        <dependency>
            <groupId>org.glassfish.common</groupId>
            <artifactId>glassfish-api</artifactId>
            <version>${glassfish.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.extras</groupId>
            <artifactId>glassfish-embedded-web</artifactId>
            <version>${glassfish.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>web-embed-impl</artifactId>
            <version>${glassfish.version}</version>
            <scope>provided</scope>
        </dependency>

${glassfish.version} here is defined elsewhere in the pom to 3.0. The class at the center of it all looks like this:

public class Embedded {
    public void start() throws IOException {
        final Server server = new Server.Builder("testBuilder").build();
        ContainerBuilder containerBuilder = server.createConfig(ContainerBuilder.Type.web);
        server.addContainer(containerBuilder);
        final EmbeddedWebContainer container = (EmbeddedWebContainer) containerBuilder.create(server);
        container.setLogLevel(Level.INFO);
        server.createPort(8888);
        final EmbeddedDeployer deployer = server.getDeployer();
        DeployCommandParameters deployParams = new DeployCommandParameters();
        deployParams.contextroot = "/";     // overrides whatever the WAR contains
        File archive = new File("target/wicket-glassfish").getAbsoluteFile();
        System.out.println("Deployed: " + deployer.deploy(archive, deployParams));
    }
 
    public static void main(String[] args) throws Exception {
        new Embedded().start();
        while (true) {
            Thread.sleep(1000);
        }
    }
}

Now, that's all rather dense, I know. This code is a condensed version of a much larger, more complete version written by Alexis Moussine-Pouchkine. His blog is a great resource not only for embedded GlassFish but all sorts of GlassFish related topics. This is all that's needed to run your app on GlassFish.

There are a number of other avenues left to explore. If you want to run glassfish from maven, you'll want to probably look at this entry. You can also enable some security as noted here. For the definitive word on the matter, however, please see the formal documentation for embedded glassfish here.

16Feb/10Off

Grizzly and WebSockets

Posted by admin

As HTML5 lumbers its way through the standardization process, more and more developers are starting to play with the emerging features.  One feature getting some serious attention is that of the websocket.  Full details of the websocket protocol can be found here but I'll lay out the basics of it.  Essentially a websocket is a TCP based socket that can be opened from a webpage via, typically, javascript code allowing bidirectional communication between the browser and the server.  This allows for comet-like interactions but with an extra benefit (or two).  Once the websocket connection is established there are no more protocol negotiations and handshakes unlike your typical AJAX conversations.  And unlike (most?) comet implementations, a websocket can process multiple requests from the client.  Obviously, this can lead to some rather interesting use cases.

It's early yet so support for websockets on either end of the browser/server connection is spotty at best.  But we're staring to see browser support emerge and a number of server side options are popping up as well.  This morning I committed an early rough draft, if you will, for support inside the grizzly project and soon glassfish itself.  It's a mostly complete implementation and is ready for some experimentation.  At the moment, the sample in the unit tests that will be of the most interest is a servlet based approach.  What's nice about the current approach is that the servlet is largely unaware that it's involved in a websocket transaction at all.  There's a lot of value in something like that but might limit some other, more powerful, use cases.

For now, though, you can play with the unit tests and see what can be done.  It's preliminary but working and the API will evolve as we get feedback from the community.  We'll be discussing how best to expose this functionality without needing to know all that much about grizzly internals.  We'll be looking at other cases such as jetty and atmosphere to make sure we can provide a smooth, useful API that most people are comfortable with.  There's been talk at various levels of building some form of standard interface to plug in to various websockets implementations on the server side but until then, we'd love your help in building something with grizzly and glassfish that we can all use.

So please join us on the mailing lists and give us a hand.

Technorati Tags: , , ,