Alexey has recently blogged on using JSP with Grizzly 2.3. See this entry for details.
Grizzly 2.3+SPDY/3
We announced earlier in the week that we released Grizzly 2.3! One of the major features of this release is the inclusion of SPDY/3 support.
SPDY is, per the following quote from Wikipedia:
The goal of SPDY is to reduce web page load time. This is achieved by prioritizing and multiplexing the transfer of web page subresources so that only one connection per client is required.
Another good quote:
SPDY does not replace HTTP; it modifies the way HTTP requests and responses are sent over the wire. This means that all existing server-side applications can be used without modification if a SPDY-compatible translation layer is put in place. When sent over SPDY, HTTP requests are processed, tokenized, simplified and compressed.
You can read all of the nitty-gritty details of SPDY here.
SPDY also typically is only used with transport layer security (TLS) using an extension called Next Protocol Negotiation (referred to as NPN). In a nutshell, this allows application-level protocols to be negotiated during the SSL handshake for easy tunnelling. See this RFC draft for more details on NPN.
In order for the Grizzly SPDY/3 implementation to function, you’ll need our SPDY and NPN runtimes.
The one gotcha right now with NPN and Java is that the SSL runtime does not natively support it. So initially we decided on using Jetty’s NPN implementation. This worked fine with our initial implementation, however, due to potential red-tape issues, we decided to roll our own. The end result, is fairly similar to Jetty’s due to how we bridged to their implementation. As such we have similar restrictions. As of this time, we only support SPDY/3 when using OpenJDK 7u14. Versions later than this may or may not work as the SSL internals appear to change frequently as we’re replacing key SSL classes via the JVM bootclasspath mechanism. If you do find a bug or an incompatibility with later versions of OpenJDK, please let us know and log an issue.
The maven coordinates for everything you need are:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!-- This is the Grizzly/SPDY 3 runtime --> <dependency> <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-spdy</artifactId> <version>2.3</version> </dependency> <!-- This is the JAR that will be placed on the bootclasspath in order for NPN to work --> <dependency> <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-npn-bootstrap</artifactId> <version>1.0</version> </dependency> <!-- This JAR is an extension bundle that will allow the Grizzly specific NPN classes to be exposed properly to an OSGi environment such as GlassFish 4 --> <dependency> <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-npn-osgi</artifactId> <version>1.0</version> </dependency> |
So, how does one use SPDY with Grizzly 2.3 standalone? It’s pretty simple to configure.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
HttpServer httpServer = new HttpServer(); NetworkListener listener = new NetworkListener("grizzly", NetworkListener.DEFAULT_NETWORK_HOST, PORT); listener.setSecure(true); // Include environmental specific SSL configuration. listener.setSSLEngineConfig(...); SpdyAddOn sdpyAddon = new SpdyAddOn(); // optionally configure listener.registerAddOn(spdyAddon); httpServer.addListener(listener); // Add your HttpHandler implementations and start the server ... |
Before you actually start the server, the grizzly-npn-bootstrap.jar needs to be added to the JVM’s bootclasspath. For example, if the JAR was in my home directory, then the bootclasspath would look something like: -Xbootclasspath/p:/Users/catsnac/grizzly-npn-bootstrap-1.0.jar.
How do you know it’s working? Well, there’s a couple possibilities. Chrome, for example, has an extension that indicates whether or not the site you’re visiting is using SPDY or not. A more primitive option, which I’ll be showing here, is using the -Djavax.net.debug=ssl:handshake JVM option on the server-side.
Simply start the standalone Grizzly/SPDY application with the aforementioned ssl debug option and connect using an SPDY-enabled browser. Assuming the Grizzly NPN implementation is properly set in the bootclasspath, you should see:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
*** ClientHello, TLSv1.1 . . . Extension server_name, server_name: [host_name: localhost] Extension renegotiation_info, renegotiated_connection: <empty> Extension elliptic_curves, curve names: {secp256r1, secp384r1, secp521r1} Extension ec_point_formats, formats: [uncompressed] Unsupported extension type_35, data: Next Protocol Negotiation Extension [0x3374/13172], protocols: [], compressed data: (null) <=== NPN Extension is in use! Unsupported extension type_30031, data: *** . . . *** ServerHello, TLSv1.1 RandomCookie: GMT: 1365183826 bytes = { 91, 138, 46, 145, 109, 200, 131, 90, 237, 8, 189, 92, 245, 46, 130, 108, 126, 190, 24, 137, 142, 139, 208, 91, 217, 105, 189, 203 } Session ID: {81, 95, 13, 82, 192, 95, 210, 250, 208, 203, 102, 234, 156, 206, 244, 0, 26, 208, 117, 149, 249, 12, 0, 174, 230, 171, 13, 105, 238, 98, 253, 17} Cipher Suite: SSL_RSA_WITH_RC4_128_SHA Compression Method: 0 Extension renegotiation_info, renegotiated_connection: <empty> Next Protocol Negotiation Extension [0x3374/13172], protocols: [spdy/3, http/1.1], compressed data: 06:73:70:64:79:2f:33:08:68:74:74:70:2f:31:2e:31 <=== Server is sending supported protocols! . . . Server write key: 0000: 0C C8 23 C7 52 1F A0 88 88 00 DC B9 41 6C 50 07 ..#.R.......AlP. ... no IV derived for this protocol Grizzly-kernel(8) SelectorRunner, READ: TLSv1.1 Change Cipher Spec, length = 1 Grizzly-kernel(8) SelectorRunner, READ: TLSv1.1 Handshake, length = 72 NPN selected protocol is: spdy/3 <=== SPDY/3 has been negotiated! *** Finished |
It may be difficult to see, but if you search for “<===” in the content above, you can see where I’ve pointed out the specific NPN messages.
So, what about GlassFish? We do have integration code for GlassFish 4(we recommend using the latest promoted builds). There are a few extra steps to getting it working in that environment.
Like the standalone example, you’ll need to add the same bootclasspath definition to the domain.xml. You’ll also need to copy the grizzly-npn-osgi-1.0.jar and grizzly-spdy-2.3.jar to $GF_HOME/modules. The grizzly-spdy module includes a command to enable spdy. So once the jars are in place, the bootclasspath updated, you can run the enable-spdy command against the secure HTTP listener. Assuming you’re doing this against the default configuration, this would be http-listener-2: asadmin enable-spdy http-listener-2.
Once the above is done, you can ensure it’s working in the same way as was previously described.
What about tuning? At this point in time we offer the following configuration options. These can be set either via the SpdyAddon (Grizzly standalone) or using asadmin set.
| maxConcurrentStreams | Configures how many streams may be multiplexed over a single connection. The default is 100. |
| initialWindowSizeInBytes | Configures how much memory each stream will consume on the server side. The default is 64KB. |
| maxFrameLength | Configures the upper bound on allowable frame sizes. Frames above this bound will be rejected. The default is 2^24 bytes in length – the max allowed by the spec. |
Lastly, there are some limitations that should be spelled out. Please see our documentation for details. We’d like to encourage you give it a shot and log any problems or features you like to see resolved/implemented.
Enjoy!
Wireshark – Dissecting WebSocket Conversations
I’ve been a long time fan of Wireshark for sniffing HTTP traffic between client/server. I’ve even used it to track down the IP address of a host flooding a network with UDP packets. It’s just a useful tool all around. When working on getting the Grizzly WebSocket implementation passing the Autobahn test suite however, there was no dissector (the entity responsible for parsing a “network packet”) for WebSockets – so it was a little tedious at times trying to pick the frames apart manually. However, it seems in one of the recent updates (I’m using 1.8.1 by the way), a WebSocket dissector was added. Needless to say, I was very happy to see this.
Here’s an example of a simple conversation captured with Wireshark:

Here you can see the overview of the conversation. The initiating GET, followed by the request being upgraded to a WebSocket connection, followed by several websocket frames (both client – MASKED, and server).
Typically, you may be interested in a particular conversation. If you’re capturing all data to a particular port, it can be difficult to follow a particular conversation. WireShark makes this an easy problem to deal with. You can right-click on a particular packet and follow the stream your interested in:

Which will yield the raw representation of the entire stream:

One other thing to notice is that once you close this window out, this conversation will be the only one in the overview that we showed earlier – the other conversations have now been filtered out.
This is all well and good, but the raw view isn’t particularly helpful when looking at the websocket frames. In order to drill down into the frame details, you can select the frame of interest in the overview, and then drill down to the nitty gritty details:

The fin bit, opcode, mask bit, etc. is all there for easy inspection including the masked and unmasked payload. Pretty handy for debugging if you ask me.
Grizzly, Async HTTP Client, Atmosphere, and Android
I’ve realized that this is only my second entry for 2012 – it certainly doesn’t seem like it’s been that long since January. I’ll try to be better about posting more often. That having been said, for some time I’ve been wondering if Grizzly could work on Android. More specifically, if Async HTTP Client’s Grizzly provider could be used as a websocket client on that platform. I’ve recently had some time to find out.
DISCLAIMER: I am *not* an Android developer, so don’t be surprised if the code I show is not inline with the usual best practices when developing on that platform. I just wanted a quick and dirty proof of concept.
So for the test, I used the following software packages:
- Async HTTP Client (referred to as AHC for the remainder of this entry) 1.7.6 (This depends on Grizzly 2.2.16)
- Atmosphere 1.0.0 (specifically, the rest-chat sample)
- GlassFish 3.1.2.2
I’m not going to go into too many details on the GlassFish/Atmosphere side of things outside of make sure comet and websocket support is enabled for http-listener-1, start the server, and deploy the atmosphere-rest-chat web application to GlassFish.
Now for the Android side of things, which, as I was not familiar with programming on Android, turned out to be entertaining to do. I used Intellij 12′s Android support to develop the UI and the Activity class which would handle all of the interactions for a simple websocket client. This client performs four basic functions: connect, disconnect, send messages, display messages.
The implementation of these functions was performed by two different AsyncTask implementations (see the Android developer SDK documentation for more details). Connect/Disconnect and message display were merged into one task since the AHC WebSocketListener implementation was added at the time of connection.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
private class ConnectDisconnectTask extends AsyncTask { private Button button; private TextView textView; protected ConnectDisconnectTask(Button button, TextView textView) { this.button = button; // connect/disconnect button this.textView = textView; // TextView instance to display messages to } ProgressDialog mProgressDialog; @Override protected void onPostExecute(Void result) { mProgressDialog.dismiss(); // dismiss the connection dialog } @Override protected void onPreExecute() { mProgressDialog = ProgressDialog.show(MyActivity.this, "Connecting...", "Connecting..."); // connection progress } @Override protected Void doInBackground(Void... voids) { if (connected.get()) { // if we're connected, close the socket and client down if (webSocket != null) { webSocket.close(); webSocket = null; } if (client != null) { client.close(); client = null; } // All IO is performed on a background thread - UI updates from said thread // must be performed using the runOnUiThread() call. runOnUiThread(new Runnable() { public void run() { button.setText("Connect"); } }); connected.compareAndSet(true, false); } else { AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); client = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); try { webSocket = client.prepareConnect("ws:10.0.1.102:8080/atmosphere-rest-chat/chat") .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() { public void onOpen(WebSocket webSocket) { LOGGER.info("Connected: " + webSocket); } public void onClose(WebSocket webSocket) { LOGGER.info("Closed: " + webSocket); } public void onError(Throwable throwable) { LOGGER.severe("Error: " + throwable); } // This will be invoked upon each broadcast from Atmosphere on the server // side. public void onMessage(final String s) { // All IO is performed on a background thread - UI updates from said thread // must be performed using the runOnUiThread() call. runOnUiThread(new Runnable() { public void run() { try { // Message will be json, so use Android's built-in json support JSONObject obj = new JSONObject(s); textView.append((obj.get("author") + ": "; + obj.get("text") + '\n')); } catch (Exception e) { onError(e); } } }); } public void onFragment(String s, boolean b) { } }).build()).get(); } catch (Exception e) { LOGGER.log(Level.SEVERE, e.toString(), e); if (client != null) { client.close(); client = null; return null; } } // All IO is performed on a background thread - UI updates from said thread // must be performed using the runOnUiThread() call. runOnUiThread(new Runnable() { public void run() { button.setText("Disconnect"); } }); connected.compareAndSet(false, true); } return null; } } |
The other task is the SendMessage task which will be invoked when the ‘Send’ button is pressed:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private class SendMessageTask extends AsyncTask { private final String message; public SendMessageTask(String message) { this.message = message; // message from the input field } @Override protected Void doInBackground(Void... args) { JSONObject obj = new JSONObject(); try { // populate the json object final String text = message; obj.put("text", text); obj.put("author", "android"); obj.put("time", new Date().getTime()); } catch (Exception e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); return null; } webSocket.sendTextMessage(obj.toString()); return null; } } |
The last bit of info you’ll need to make this work is that a special configuration option is needed in the AndroidManifest.xml in order for network IO to be allowed:
|
1 |
With the code above (well, most of it anyway) in place we should be able to have a chat conversation between Android and browser clients:
Here is the browser’s initial state after entering the user’s chat name:

Here is the Android client’s initial state after connecting and sending a message:

And now the browser’s view:

And then what Android sees when the browser responds:

There! Not too difficult, right?
I found it satisfying to know that AHC+Grizzly may be used as a client solution for custom websocket applications on Android. Hopefully with this basic information being made available, it will prompt more users to explore this option.
Lastly, if there is any interest in having the complete code to play around with, follow up to the post and if there’s enough interest, I’ll push the code to github.
Async HTTP Client 1.7.0 Released. Details on the Grizzly Side of Things…
Unfortunately, this blog entry is lagging behind in terms of when the release occurred. Earlier this month (1/11), Jean-Francois released Async HTTP Client 1.7.0.
Quoting his release annoucement:
This release is our first with the Grizzly 2 provider, which supports
the same set of features as Netty (and performance wise match Netty
without any problems). This release also contains our first drop of the
WebSocket support (both Netty and Grizzly).
In a previous blog entry I offered some brief examples on using the AHC with the Grizzly provider. This entry also included some maven coordinates for a bundle containing only Grizzly dependencies.
These coordinates have changed. For those interested in this bundle, use the following maven coordinates instead:
|
1 2 3 4 5 |
<dependency> <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-http-client</artifactId> <version>1.0</version> </dependency> |
Note that we’ll only be maintaining this bundle until such time that the Sonatype Async HTTP Client project modularizes the api and providers into distinct bundles.
With that out of the way, the other new feature available is WebSockets support. Let’s review the moving parts.
The UpgradeHandler
The UpgradeHandler is a general handler/contract for any type of HTTP upgrade request. For WebSockets, there is a concrete implementation called WebSocketUpgradeHandler. Knowledge of the underlying workings of this handler aren’t really necessary. All that you need to remember is that this handler must be passed to the AsyncHttpClient.execute() method. Instances of this handler are created using its associated builder, WebSocketUpgradeHandler.Builder which defines methods that allows the developer to add/remove listeners and perform WebSocket customization:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public final static class Builder { /** * Add a {@link WebSocketListener} that will be added to the {@link WebSocket} * * @param listener a {@link WebSocketListener} * @return this */ public Builder addWebSocketListener(WebSocketListener listener) { ... } /** * Remove a {@link WebSocketListener} * * @param listener a {@link WebSocketListener} * @return this */ public Builder removeWebSocketListener(WebSocketListener listener) { ... } /** * Set the WebSocket protocol. * * @param protocol the WebSocket protocol. * @return this */ public Builder setProtocol(String protocol) { ... } /** * Set the max size of the WebSocket byte message that will be sent. * * @param maxByteSize max size of the WebSocket byte message * @return this */ public Builder setMaxByteSize(long maxByteSize) { ... } /** * Set the max size of the WebSocket text message that will be sent. * * @param maxTextSize max size of the WebSocket byte message * @return this */ public Builder setMaxTextSize(long maxTextSize) { ... } } |
The behavior properties should be self-explanatory, we’ll touch on WebSocketListeners next.
The WebSocketListener
WebSocketListener is the base interface for all WebSocketListener implementations.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public interface WebSocketListener { /** * Invoked when the {@link WebSocket} is open. * @param websocket */ void onOpen(WebSocket websocket); /** * Invoked when the {@link WebSocket} is close. * @param websocket */ void onClose(WebSocket websocket); /** * Invoked when the {@link WebSocket} is open. * @param t a {@link Throwable} */ void onError(Throwable t); } |
From there, AHC separates listener functionality into text, binary, ping, and pong listeners.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public interface WebSocketTextListener extends WebSocketListener { /** * Invoked when WebSocket text message are received. * @param message a {@link String} message */ void onMessage(String message); /** * Invoked when WebSocket text fragments are received. * * @param fragment text fragment * @param last if this fragment is the last of the series. */ void onFragment(String fragment, boolean last); } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public interface WebSocketByteListener extends WebSocketListener { /** * Invoked when bytes are available. * @param message a byte array. */ void onMessage(byte[] message); /** * Invoked when bytes of a fragmented message are available. * * @param fragment byte[] fragment. * @param last if this fragment is the last in the series. */ void onFragment(byte[] fragment, boolean last); } |
|
1 2 3 4 5 6 7 8 9 |
public interface WebSocketPingListener extends WebSocketListener { /** * Invoked when a ping message is received * @param message a byte array */ void onPing(byte[] message); } |
|
1 2 3 4 5 6 7 8 9 |
public interface WebSocketPongListener extends WebSocketListener { /** * Invoked when a pong message is received * @param message a byte array */ void onPong(byte[] message); } |
For developer convenience, we’ve added DefaultWebSocketListener that implements all interfaces where all methods (except onOpen/onClosed) are no-ops allowing behavior override only as needed.
The WebSocket
The final interface of interest is WebSocket.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
public interface WebSocket { /** * Send a byte message. * @param message a byte message * @return this */ WebSocket sendMessage(byte[] message); /** * Allows streaming of multiple binary fragments. * * @param fragment binary fragment. * @param last flag indicating whether or not this is the last fragment. * * @return this. */ WebSocket stream(byte[] fragment, boolean last); /** * Allows streaming of multiple binary fragments. * * @param fragment binary fragment. * @param offset starting offset. * @param len length. * @param last flag indicating whether or not this is the last fragment. * @return */ WebSocket stream(byte[] fragment, int offset, int len, boolean last); /** * Send a text message * @param message a text message * @return this. */ WebSocket sendTextMessage(String message); /** * Allows streaming of multiple text fragments. * * @param fragment text fragment. * @param last flag indicating whether or not this is the last fragment. * @return this. */ WebSocket streamText(String fragment, boolean last); /** * Send a <code>ping</ping> with an optional payload * (limited to 125 bytes or less). * * @param payload the ping payload. * * @return this. */ WebSocket sendPing(byte[] payload); /** * Send a <code>ping</ping> with an optional payload * (limited to 125 bytes or less). * * @param payload the pong payload. * @return this. */ WebSocket sendPong(byte[] payload); /** * Add a {@link WebSocketListener} * @param l a {@link WebSocketListener} * @return this */ WebSocket addMessageListener(WebSocketListener l); /** * Returns <code>true</code> if the WebSocket is open/connected. * * @return <code>true</code> if the WebSocket is open/connected. */ boolean isOpen(); /** * Close the WebSocket. */ void close(); } |
So, a simple example putting all the parts together might look something like:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// This example will send a WS message to a server that echoes the message back AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); AsyncHttpClient c = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config)); String wsUrl = "ws://somehost/wspath"; WebSocketListener listener = new DefaultWebSocketListener() { @Override onMessage(String message) { System.out.println("Received message: " + message); } }; WebSocketUpgradeHandler handler = new WebSocketUpgradeHandler().Builder().addWebSocketListener(listener).build(); WebSocket socket = c.prepareGet(wsUrl).execute(handler).get(); socket.sendMessage("Hello!".getBytes("UTF-8")); |
Final Notes
The Grizzly provider implements the final version of the WebSockets RFC. However, at this point in time, the client will not try to fall back to older versions of the protocol if the server doesn’t support the same version as the client. This is something I hope we can resolve in the next release.
Grizzly 2.2 now available!
We’ve finally released 2.2! There’s a few features/changes I’d like to highlight here.
I’ll start with the fact that this release is not binary compatible with 2.1. Items to be aware of:
- CloseListener interface updated in order allow developers to distinguish between a local or remote close.
- Methods that accept CompletionHandlers no longer return Futures and methods that return Futures no longer accept CompletionHandlers. This was a fairly large change so I won’t go into which files were touched. While we understand this may be a point of frustration, we did reap some performance benefits and added API clarity from doing so. Please see revision 0d5f62 for details and how this may impact your project.
With the not-so-pleasant part out of the way, let’s talk about the fun stuff that’s been added.
Non-Blocking Sendfile Support
We now can support FileChannel.transferTo() to send files to a socket. This feature is available in the core framework and is easy to use. See the following for details:
- FileTransfer represents the intent to send a file via FileChannel.transferTo(). Simply create the instance, write it to the connection.
- FileTransferTest.java offers a concrete example of this feature.
We’ve also exposed this feature within the http-server module. Sendfile support will be enabled automatically if the underlying platform will support it properly. Sendfile support will be available if the underlying OS is not HP-UX or if the OS is Linux and the underlying JDK is 1.6.0_18 or newer. However, you can force the issue by enabling/disabling the feature via NetworkListener.setSendFileEnabled(boolean).
We’ve taken a page out of Tomcat’s playbook with how we’ve exposed this feature to developers. I’ll let the documentation explain…
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/** * Request attribute will be associated with a boolean value indicating * whether or not it's possible to transfer a {@link java.io.File} using sendfile. * * @since 2.2 */ public static final String SEND_FILE_ENABLED_ATTR = "org.glassfish.grizzly.http.SEND_FILE_ENABLED"; /** * * The value of this request attribute, as set by the developer must be a {@link java.io.File} * that exists, is not a directory, and is readable. This {@link java.io.File} will be * transferred using sendfile if {@link #SEND_FILE_ENABLED_ATTR} is true. If sendfile * support isn't enabled, an IllegalStateException will be raised at runtime. * The {@link HttpHandler} using this functionality should refrain from writing content * via the response. * * Note that once this attribute is set, the sendfile process will begin. * * @since 2.2 */ public static final String SEND_FILE_ATTR = "org.glassfish.grizzly.http.SEND_FILE"; /** * The value of this request attribute signifies the starting offset of the file * transfer. If not specified, an offset of zero will be assumed. The type of * the value must be {@link Long}. * * NOTE: In order for this attribute to take effect, it must be * set before the {@link #SEND_FILE_ATTR} is set. * * @since 2.2 */ public static final String SEND_FILE_START_OFFSET_ATTR = "org.glassfish.grizzly.http.FILE_START_OFFSET"; /** * The value of this request attribute signifies the total number of bytes to * transfer. If not specified, the entire file will be transferred. * The type of the value must be {@link Long} * * NOTE: In order for this attribute to take effect, it must be * set before the {@link #SEND_FILE_ATTR} is set. * * @since 2.2 */ public static final String SEND_FILE_WRITE_LEN_ATTR = "org.glassfish.grizzly.http.FILE_WRITE_LEN"; |
So a typical example would look like:
|
1 2 3 4 5 6 7 8 9 10 |
@Override public void service(final Request request, Response response) throws Exception { if ((Boolean) request.getAttribute("org.glassfish.grizzly.http.SEND_FILE_ENABLED")) { request.setAttribute("org.glassfish.grizzly.http.SEND_FILE", new File(..)); } else { // your favorite byte-copy method goes here... } } |
Grizzly 2.2 Transport for Apache Thrift
Contributed by Bongjae Chang. Please see Bongjae’s blog entry on this topic for details.
RFC 6455: The Websocket Protocol
We’re up-to-date with respect to the Websocket RFC. We can somewhat back up this claim by stating 2.2 passes the Autobahn Websocket Server Test Suite. I’ll be sure to either post the results on our project page, or see if I can talk the Autobahn folks into including the results on their site as they do for Jetty.
Write I/O Thottling
We’ve added a new interface to allow throttling of write I/O to prevent overloading the async write queue and potential OOM situations
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/** * Callback handler, which will be called by Grizzly {@link org.glassfish.grizzly.Writer} * implementation, if message can not be neither written nor added to write queue * at the moment due to I/O or memory limitations. * User may perform one of the actions proposed by {@link PushBackContext} or * implement any other custom processing logic. * * @since 2.2 * * @author Alexey Stashok */ public interface PushBackHandler { /** * The method is invoked once message is accepted by * {@link org.glassfish.grizzly.Writer}. It means either message was written * or scheduled to be written asynchronously. * * @param connection {@link Connection} * @param message {@link WritableMessage} */ public void onAccept(Connection connection, WritableMessage message); /** * The method is invoked if message was refused by {@link org.glassfish.grizzly.Writer} * due to I/O or memory limitations. * At this point user can perform one of the actions proposed by {@link PushBackContext}, * or implement any custom processing logic. * * @param connection {@link Connection} * @param message {@link WritableMessage} * @param pushBackContext {@link PushBackContext} */ public void onPushBack(Connection connection, WritableMessage message, PushBackContext pushBackContext); } |
Our TunnelFilter.java sample shows this Interface in action.
Async HTTP Client
The next version of Async HTTP Client, 1.7.0, is close to being released. This will be the first release that offers a Grizzly-based provider (based on Grizzly 2.2). However, a new feature worth mentioning here is that this release will include WebSocket support! Again, taking advantage of the work by the Autobahn folks, we’ve run our WebSocket client implementation against their test suite and have passed. Once 1.7.0 goes final, I’ll create another blog entry with details!
Improved Performance
I touched on performance briefly when I covered binary compatibility. We’ll post some numbers in a follow up once we’ve been able to distill and generate some graphs.
I think that about covers it. The full change log for this release can be found here. Questions or concerns? Please feel free to contact us via the mailing lists!
Grizzly 2.2 Async HTTP Client
Many of you may already be familiar with the Async HTTP Client Library project originally started by Jean-Francois Arcand during his tenure at Ning. One of the many nice features offered by this API is the ability to plug in different network libraries. Past releases of the library included providers for the JDK, Apache, and Netty. This past June, we donated a provider implementation based on Grizzly 2.1.2.
The Grizzly implementation, as it currently stands, should be considered beta quality. On going work is happening here. So if anyone wishes to contribute patches, please fork that repository and issue pull requests. In turn, we’ll continue to send pull requests to the Sonatype folks (at least until we are granted privileges to commit directly).
In addition to this, we will be providing a bundle of our own that includes the HTTP Codec library (grizzly-http) as well as the AHC library and includes only the Grizzly provider. Currently, this bundle is only available as a snapshot, but will be included as part of the final Grizzly 2.2 release.
To start kicking the tires of the bundle, a few preliminary steps are required. First, you’ll need to add a repository element for the Java.Net maven snapshot repository.
|
1 2 3 4 |
<repository> <id>java.net.snaps</id> <url>https://maven.java.net/content/repositories/snapshots/</url> </repository> |
Next, add the dependency to pull in the snapshot.
|
1 2 3 4 5 |
<dependency> <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-http-client</artifactId> <version>2.2-SNAPSHOT</version> </dependency> |
With that out of the way, you can start playing. A trivial example:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
final AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); AsyncHttpClient client = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); final Request request = new RequestBuilder("GET").setUrl("http://www.google.com/").build(); try { Future responseFuture = client.executeRequest(request, new AsyncCompletionHandler() { @Override public Response onCompleted(Response response) throws Exception { return response; } @Override public void onThrowable(Throwable t) { t.printStackTrace(); } }); final Response response = responseFuture.get(5, TimeUnit.SECONDS); System.out.println("Headers:"); for (final Map.Entry> entry : response.getHeaders().entrySet()) { System.out.printf("\t%s : %s\n", entry.getKey(), entry.getValue().toString()); } System.out.printf("\nBody length: %d\n", response.getResponseBody().length()); } catch (Exception e) { e.printStackTrace(); } finally { client.close(); } |
Documentation appears to be somewhat scattered at the moment. See:
If, when testing, you find any issues with the Grizzly provider, please log them here using the http client component. We’ll address them ASAP.
Grizzly 2.1.2 has been released!
The Grizzly community has released 2.1.2!
2.1.2 Feature Guide
- Bundles! For developer convenience, we’ve added several bundles that are aggregates of existing modules.
- grizzly-core
- This bundle is comprised of the grizzly-framework, grizzly-portunif, and grizzly-rcm jars.
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-core.
- grizzly-http-server-core
- This bundle is comprised of the grizzly-core bundle, as well as the grizzly-http-server, grizzly-http-ajp, and grizzly-http-server-multipart jars.
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-http-server-core.
- grizzly-comet-server
- Comprised of: the grizzly-http-server-core bundle as well as grizzly-comet jar.
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-comet-server.
- grizzly-websockets-server
- Comprised of: the grizzly-http-server-core bundle as well as grizzly-websockets jar.
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-websockets-server.
- grizzly-http-servlet-server
- Comprised of: the grizzly-http-server-core bundle as well as grizzly-http-servlet jar.
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-http-servlet-server.
- grizzly-http-all
- Comprised of: all bundles listed above
- Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-http-all.
- grizzly-core
- Re-introduction of the Grizzly OSGi HTTP Service.
- Maven coordinates for the standalone jar: groupId: org.glassfish.grizzly.osgi, artifactId: grizzly-httpservice.
- For developer convenience there is also a bundle available that includes the JAR above as well as exposing HTTP Servlet and Comet support. Maven coordinates: groupId: org.glassfish.grizzly.osgi, artifactId: grizzly-httpservice-bundle.
- JAXWS Server
- HTTP+JAXWS enabled server. Maven coordinates: groupId: org.glassfish.grizzly, artifactId: grizzly-http-server-jaxws.
- Improved WebSockets support
- Supports WebSockets drafts -76 through -08. The version to be used for communication will be selected based on the handshake. If it can’t be detected, then -76 will be selected.
- We’re on Maven Central! Feel free to remove the old java.net maven repository references.
In addition to these features, there have been numerous bug and performances fixes. See the release notes for details.
Looking forward, here is what we have planned for 2.2:
- Async HTTP Client. This client is a a Grizzly 2.x implementation of Sonatype’s Async HTTP Client. The initial code has already been donated, but we plan to offer a bundle that includes only the Grizzly and JDK providers. The code is still in an alpha state and would love to obtain some feedback from the community, so if you’re already using the AHC or are shopping around for a HTTP client library, please consider giving this implementation a shot.
- FileChannel.transferTo() support (referred to as “sendfile” support). This feature is currently in progress and will allow for efficient file transfers at the Grizzly core framework level. We’ll then add code to take advantage of this feature at the HTTP level.
- Google Protocol Buffer support. This feature is baked, but we’re waiting on some red tape to be cut before we can integrate. You can view the code here.
We, the community, hope you find this release useful. If you have any problems with this release, or have some ideas for improvements or new features, please log an issue as appropriate. Several of the project developers can also be found on irc.freenode.net in the #grizzly channel. Stop by and chat. However, if IRC isn’t your style, the mailing lists are always an option.
Grizzly 2.1: Released into the Wild!
The Grizzly community has released 2.1. Interestingly enough, this was going to be the first patch release of 2.0, however, since this release has many new features it was deemed we should go straight to 2.1.
2.1 Feature Guide
- Apache JServ Protocol (AJP) Support – We’ve implemented the AJP protocol support via a pair of Filters. If your Grizzly-based HTTP application requires load balancing, you can simply add these filters to your FilterChain to enable the support. Further documentation can be found here.
- Multipart Form Submission Processing – This feature allows developers to process multipart form requests from a client. Documentation for this feature is here.
- LZMA Compression – While 1.9 included LZMA support for HTTP response compression, Grizzly 2.1 supports compression at the core framework level as well as bi-directionally within the HTTP framework (documentation forthcoming).
- WebSockets refresh – WebSockets support has been updated from -76 to draft 06 (documentation forthcoming).
In addition to these features, there have been numerous bug and performances fixes. See the release notes for details.
We, the community, hope you find this release useful. If you have any problems with this release, or have some ideas for improvements or new features, please log an issue as appropriate. Several of the project developers can also be found on irc.freenode.net in the #grizzly channel. Stop by and chat. However, if IRC isn’t your style, the mailing lists are always an option.