Kindle Fire: First Impressions

My Fire finally arrived last night. Although I've been anxiously awaiting this day for a while now, I think my girls were even more excited during the unboxing than I was. The new machine is beautiful. I'll just say that right out of the gate. The screen is bright and the colors are sharp. The home screen is laid out fairly well. It's vaguely reminiscent of how I remember some screenshots of Apple's book reader software. (I've never used that so I could be way off.)
The touch screen seems to be slightly less responsive than my Droid X. My droid will react to errant brushes of the screen where it seems like the Fire needs a slightly more intentional, insistent touch. This isn't entirely bad but takes a little getting used to.
As expected, Amazon's shopping services are tightly integrated. Anything you want from Amazon is just a few taps away. The apps I've bought for my android phone are there under "my apps" on the kindle, too, ready for download. I haven't done a full audit, but it would seem I can redownload any/all of them. I've already done so for a number of them but I can't guarantee 100% coverage on that front.
There is, however, one glaring failure on Amazon's part here. And, to be honest, I'm actively considering returning the Fire because of it. I knew that the Fire would be tightly integrated with Amazon's services. It was actually a mild selling point for me. I knew that Google's Android Marketplace would not be on the machine. That's a mild bummer but I'm sure that'll be rectified sooner or later. But what I find completely unacceptable is this: if I open the browser and go to market.android.com, it actually launches Amazon's app store. I understand they'd rather people buy apps from their store, but they have no right to intercept legitimate web traffic and reroute to their own sites. I should be free to browse whatever I want on the web without interference.
If Amazon doesn't fix this, I'm seriously considering returning this and paying the extra money for the Samsung Galaxy Tab 7.0 Plus. I'll take a slightly less integrated experience if it means I'm free to read whatever web pages I want. I don't need or want someone to hold my hand or dictate to me what I can or can not read on my devices. If nothing else, I'll root this thing first chance I get and run stock Android on it.
So please, Amazon, fix this. This goes well beyond allowing only your app store. Even apple doesn't block websites on the ipad. You have no right to either.
So Long and Thanks for All the GlassFish
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...
Websockets and Java EE
Work has begun on the Servlet Expert Group to come up with a standard set of atoms for WebSocket applications in a Java EE environment. I'm not 100% convinced that the servlet spec is the best place to do this but I suppose it saves us the hassle of submitting another JSR and all the work that would entail. However, that's where the work is being done for now, so whatever. This is something I've naturally thought a lot about over the last couple of years as I've worked to implement and promote WebSockets so here is roughly how I would do it.
There's a lot of "we have to have something in the servlet spec" comments floating around. I don't necessarily agree with that sentiment but there is one thing I would: support for the upgrade request header. This would have the servlet container automatically detect a request with this header. The container would then query the system for all implementations of upgrade request handlers registered. Then, based on which protocol each handler has said it deals with, hand off processing to that handler. At this point, the request is not necessarily in the servlet stack at all but a completely separate API that might not even be back by a JSR. This has multiple advantages in my mind.
- WebSockets uses this header and so this feature is already going to be needed for WebSockets to work.
- It provides a general hook and customization point at which developers can choose to extend the container without resorting to modifying the source of the container itself to handle the new protocol. More importantly, it does so in a standardized fashion allowing for portability at least among the higher levels of the API. Certain container specific code might be needed for such things as suspending/parking requests and the like. But these could easily be abstracted away such that the protocol logic itself could be fairly portable.
- This also decouples new APIs such as WebSockets from the constraints of the legacy(?) servlet API.
- Using this hook, someone could homebrew their own protocols using the upgrade mechanism and be able to integrate in to their container with little fuss.
Barring some tweaks here and there, that's all the servlet spec really needs to offer. So where does that leave WebSockets? Why, in its own JSR, of course! I really think it needs its own separate JSR with its own EG populated by folks who know and understand the technology. More importantly, this separate EG would feel less bound to make this new API consistent/compatible with the servlet API. While there is certainly a need for integration with the servlet spec for access to things like the session data, binding the two APIs would really hamstring the interesting things we could do with WebSockets. So what would all this look like? Let's look at some mostly java mockups.
First, the servlet side upgrade handler. It would look something like this. If you've used @WebServlet, this should look familiar to you.
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HttpUpgradeHandler { /** * The name of the target protocol */ String value() default {}; /** * The description of the handler */ String description() default ""; /** * The display name of the handler */ String displayName() default ""; } |
And the interface exposed for implementations:
public interface UpgradeHandler { public void doUpgrade(ServletRequest request, ServletResponse response) throws UpgradeException; } |
This provides a general mechanism for any application developer to register a new handler thus extending the functionality of the container without need for hacking the container itself. So what would one of these handlers look like? Maybe something like this:
@HttpUpgradeHandler(protocol ="websocket", urlPatterns={"/sample"}) public class SampleWebSocketContext extends WebSocketContext { @Override public void customizeHandshake(ServletRequest request, ServletResponse response) { super.customizeHandshake(request, response); // insert cookies here } @Override public WebSocketContext createContext(ServletRequest request, ServletResponse response) { return new SampleWebSocketContext(request, response); } private static class SampleWebSocket extends DefaultWebSocket { private String userName; public SampleWebSocketContext(ServletRequest request, ServletResponse response) { super(request, response); userName = request.getParameter("user"); } public String getUserName() { return userName; } } } |
By the time control gets handed off to WebSocketContext, the appropriate headers requesting the upgrade have already been found and validated against name provided in the annotation. All that's left to do here is perform the actual upgrade. In this case, I added a method to customize the handshake for cases where, perhaps, the developer wants to add a header field or validate an existing header value. So what comes from the servlet specification is that annotation, the interface, and an exception. There might be more as details are hashed out, but that's about all I would say that the servlet specification should deliver. That paves the way for a WebSocket JSR export group to define its own atoms.
Not entirely surprisingly, I imagine a WebSocket JSR would end up with something that approximates what I built in Grizzly. That said, there are certain ... rough points that need to be smoothed over but that can most easily be smoothed over by 1) an expert group and 2) user feedback. Still, here's how I see one approach at cracking this nut.
In Grizzly, we have a WebSocketApplication which is the center of the developer's world when building WebSocket applications. I've recently come to prefer WebSocketContext instead. It's largely semantic but whatever. This is where all the application logic, if there is any, would likely want to live. Between the WebSocketContext and any potential custom implementation of WebSocket, there's a lot of room of custom logic. But none of this is tied to the servlet spec beyond the initial use of the ServletRequest and ServletResponse which I think is key.
This separation allows the WebSocket JSR evolve without needing to worry about servlet evolution. More importantly, it allows innovation to happen without needing a JSR at all. Because there's a generic upgrade hand off, developers are free to extend their container in whatever crazy directions they choose. Imagine implementing a custom JMS transport over HTTP and tunneling through your web server. Or FTP. Or SNMP. Or some completely custom, proprietary protocol. This extension point takes the EE platform one huge step down the road of enabling developers to build applications on and extending the EE platform without the need for container specific hacks. It aslo, of course, paves the way for future JSRs to provide new functionality without extraneous requirements and constraints from external JSR lifecycles.
At the risk of repeating myself, I'd like to say again that this proposal is not perfect. It almost certainly has some corner cases I haven't thought of or addressed here. But it's at least along the lines of how I've been imagining something like this would work for the last couple of years. It's a start. I have the code I showed above and more in a git hub repository if you'd like to take a look in more detail. I'd love to hear any comments you might have.
Apache Wicket Cookbook
Packt recently published Igor Vaynberg's "Apache Wicket Cookbook" and I was fortunate enough to receive a review copy. I've been a Wicket user since long before it was Apache Wicket. I've developed applications ranging in complexity from simple IRC bot log viewers to conference management suites. Knowing Wicket reasonably well, I tend to not bother reading books on the subject as most of them are aimed at the beginner. And those I find just a tad boring.
What caught my eye about this book in particular, though, was the "cookbook" approach to the writing. This book does assume that you know at least the basics of Wicket. It doesn't spend any time on trying to justify Wicket's design or approach. What it does do is get you into your code quickly. The examples cover clearly a number of very common use cases and starts to fill in the corners (to borrow a phrase from Tolkien). What I appreciated most about this book is that there are so many things that we, as wicket users and developers, just do because that's How It's Done. While he doesn't start you from zero, Igor takes some time to explain a few of the whys and wherefores: the sometimes subtle implications of why things are done certain ways, the consequences and options of other approaches.
The tone of the book is light and easy. Unlike a "from scratch" style book, this one really is just a reference book of sorts. If you'd like to deepen your understanding of Wicket, you should certainly read this cover to cover. However, if you're just struggling with a topic or two, the targeted approach of the writing lets you zoom in on your problem and get back out to your app with little fuss. It's the kind of book that I think long time users will appreciate. If you use wicket to any extent, it wouldn't hurt to have a copy of this handy.
To whet your appetite, Packt Publishing has made a chapter available. Chapter 5, Displaying Data Using Data Table, can be freely downloaded directly from the publisher's site. Looking at the table of contents, you can see the book covers quite few hot spots in the development of any application:
- Chapter 1: Validating and Converting User Input
- Introduction
- Performing form-level custom validation
- Creating a custom validator
- Composing multiple validators into a single reusable validator
- Converting string inputs to objects
- Chapter 2: Getting Down and Dirty with Forms and Form Components
- Introduction
- Creating linked selectboxes
- Composing multiple form components into a single reusable component
- Preventing multiple form submits
- Protecting against spam with a CAPTCHA
- Chapter 3: Making Forms Presentable
- Introduction
- Changing form component CSS class on validation errors
- Using FeedbackPanel to output form component specific messages
- Streamlining form component presentation using behaviors
- Chapter 4: Taking your Application Abroad
- Introduction
- Storing module resource strings in package properties
- Retrieving a localized string
- Feeding dynamic localized strings to components using StringResourceModel
- Using wicket:message to output localized markup
- Overriding localized resources on a case by case basis
- Chapter 5: Displaying Data Using DataTable
- Introduction
- Sorting
- Filtering
- Making cells clickable
- Making rows selectable with checkboxes
- Exporting data to CSV
- Chapter 6: Enhancing your UI with Tabs and Borders
- Introduction
- Creating tabs with dynamic titles
- Making a tabbed panel play nice with forms
- Creating a client-side JavaScript tabbed panel
- Using borders to decorate components
- Creating a collapsible border
- Chapter 7: Deeper into Ajax
- Introduction
- Adding Ajax validation to individual form components
- Blocking until an Ajax request is complete
- Providing Ajax feedback automatically
- Chapter 8: Visualizing Data with Charts
- Introduction
- Charting with Open Flash Chart
- Feeding chart data using a SharedResource
- Responding to clicks
- Chapter 9: Building Dynamic and Rich UI
- Introduction
- Swapping components using a select box
- Creating dynamic forms
- Creating a dynamic portal layout
All told this is a great book and a great addition to your library. This is easily something I'll keep coming back to while building wicket apps.
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 theHttpRequestobject to yourCometHandlerinstance. 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 usingattach()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:
Response getResponse();void setResponse(Response response);CometContext<E> getCometContext();void setCometContext(CometContext<E> context);
Your
CometHandlerimplementation 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 aStringbut, 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.READtoCometEvent.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<NetworkListener> 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<HttpServletResponse> 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<HttpServletResponse> context = CometEngine.getEngine().getCometContext(contextPath); context.notify(null); PrintWriter writer = res.getWriter(); writer.write("success"); writer.flush(); } } |
public class CounterHandler extends DefaultCometHandler<HttpServletResponse> { 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.9.x -> https://svn.java.net/svn/grizzly~svn/trunk/code
- 2.x -> https://svn.java.net/svn/grizzly~svn/branches/2dot0
- 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.
Port Unification in GlassFish 3 – Part 4
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.
Fall Conference Update
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. ;)
Port Unification in GlassFish 3 – Part 3
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() >= 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.