Hugh Winkler holding forth on computing and the Web

Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Sunday, January 20, 2008

Clojure

Clojure is a Lisp that runs on the JVM. I agree with everything on this page describing the design rationale. Objects are overrated; functional and immutable are good. I can't wait to get a breather and figure out whether and where Clojure fits into our technology arsenal.

The real measure will be how well it can leverage the Java platform. There are plenty of great languages, but they have inconsistent support for, say, sockets, or window systems. Can I override a protected method of a Swing base class? Can I connect to an HTTPS server? JRuby has knocked Java integration out of the park, so you get all that capability for free, plus a powerful language.

Update: I think the bullet point "Extend Java in Java, consume Java from Clojure" on the rationale page suggests you can't easily write a Swing app in Clojure. Shoot.

RIA using JRuby and Web Start

I went dark over the last eight weeks, building a rich web application to integrate with our product. It's been years since I built a desktop GUI application, but I was pretty good at it once. It's not always a lot of fun. But some things a man just has to do himself.

The application does tons of vector drawing.... it draws hundreds of thousands, even millions, of line segments. Yet it needed to be responsive... scrolling, zooming, panning. It's data intensive, too. It downloads dozens of megabytes of data for each view. I needed to develop this program fast, so I needed a programming system and platform allowing me to iterate and adapt to new requirements recursively revealed. I preferred a declarative graphics language where possible, and for any procedural code, a powerful dynamic language. Ideally the program should run on all OS platforms; but the business could probably tolerate a Windows only solution, if that offered superior results. I made tradeoffs among these requirements.

I was not constrained by download footprint, a concern which eliminates Java from consideration in lots of other scenarios. Anyway, that problem is going away soon, they tell us.

So here's the spoiler: I chose Java Web Start + JRuby. And now a short dissertation on how I arrived at that choice:

I looked to browser and browser++ technologies like SVG, Flex, AIR, Silverlight, Java applets/Web Start/JavaFX. (Open Laszlo deserves a look too, but I didn't eval it). It would be great for this app to run within the browser -- that would be the most seamless experience. But using an out-of-browser technology like AIR or Web Start isn't a deal breaker if you do it right.

SVG was very promising. It would be a pure browser solution. I really valued the declarative model and the standards alignment. But it does not work in IE. I also did some performance experiments with Opera, Safari, and Firefox. Drawing polylines of a hundred thousand points or so, Opera and Safari performed well, but Firefox performed poorly. It's conceivable, if unlikely, that I could distribute the app as a FF-only solution -- even if I bottle it up as a a XULRunner app. But the FF performance was inadequate. And distributing only for Safari or Opera is not realistic.

I made a significant evaluation effort with Flex/AIR. Surprisingly I learned that the Flex 2-D graphics model is procedural, not declarative. You can, however, bottle up your procedures and use them declaratively from mxml, the Flex XML language. I felt I was going to spend a significant amount of time learning Flex -- time I needed to spend on my app -- and that I needed the assistance of tools like Flexbuilder. I also became concerned that I was developing a program away from the design center of Flex. Would I encounter design/ performance constraints?

Silverlight performed very well, and it's got an XML declarative model just like SVG, with the power of a bunch of WPF components to boot. I was confident that all the capabilities of the Windows platform would be available to my program. Microsoft distributes Silverlight for Windows and Mac, but not Linux, which they are leaving to Novell. I read that as: No Linux. But Windows and Mac are enough coverage, even if I would be sorry to leave aside my Ubuntu dev machine. My fate on the Mac would be in Redmond's hands, of course. Does Microsoft have my best interests at heart ?

Java applets or Web Start, using Java 2D, I also knew to be capable of these rendering tasks. I'm satisfied that I can make Java applications now that look pretty sweet. The down sides were the procedural approach and the static Java language. It's the 1999 state of the art. Layout code so impenetrable you need GUI builders.

JavaFX deserves a separate mention. The capabilities of the entire Java platform are there for you. Yet you program in a declarative/dynamic language optimized for building Java 2D apps. Perfect! Unfortunately, it's pre-alpha. Sun hasn't yet released it under a license allowing you to put your work into a customer's hands.

I wanted the wide reach of Java. I think that the 5% of customers on non-Windows platforms may be more important than their numbers.

I also wanted the enormous hardened Java platform. It's as capable as the Windows platform. You won't encounter many problems you can't address or workaround.

Couldn't I have all that, plus a dynamic language to stitch it all together?

Guess what. JRuby is stable and 1.x. It's dynamic, yet it compiles on the fly to JVM byte codes. It's got great Java integration, meaning you can invoke any Java API. Profligacy is a terse little Ruby lib for stitching up Swing components. JRuby's just a jar I download with my Web Start app, and launch with the main Ruby script from my Java entry point. The rest of the program is in Ruby, occasionally calling into Java libraries. No, there's no declarative GUI language here. It's one of the tradeoffs.

My work environment: Fire up NetBeans for its good Ruby editor. In a command window kick off jirb and execute a script to load and run my main code. Edit code and reload it into the running program in jirb. Lather, rinse, repeat.

I would argue that JRuby accelerated my application's performance, because I iterated and applied two major architectural optimizations that would have introduced impenetrable spaghetti in Java. The bottleneck for speed and responsiveness has never been JRuby. It turned out that even Java2D + acceleration could not keep up acceptably with the level of interactivity I wanted. The SVG or any other pure declarative approach would have failed (although I think JavaFX's scene graph might be able to optimize the drawing). I had to go in and surgically rearrange the innards of the program. The changes were effective but had low impact on stability, thanks to the flexibility and power of Ruby.

If Sun can focus more energy on these dynamic languages built on the JVM, that would be a pretty powerful story: The deep platform capabilities, the broad reach, the rich development environment.

Wednesday, August 15, 2007

Restful Servlets + JSP: My framework

Stefan and Bill asked, so here's how I do it (with a tip of the hat to Django).

It is a micro framework. You subclass RestServlet and declare some URL patterns to match, and handlers for them. The base class parses the URI, sets attributes in the ServletRequest object based on the URI pattern, and invokes your handlers.

So here's how a simple BlogServlet would look:

[updated: fixed path to JSP, added a note above about attributes].
[update: added Apache license]

Copyright 2007 Wellstorm Development, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.



public class BlogServlet extends RestServlet{


/**
* GET an entry. The base class will populate the "entryId" attribute before calling invoke.
* We told it to do so below, when we defined entryIdentifier.
*/
RequestHandler entryGetHandler = new RequestHandler(){

public void invoke(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// The base class has parsed the URI and populated the request with
// the "entryId" attribute
String entryId = (String)request.getAttribute("entryId");
String forward = "/WEB-INF/entry.jsp";
request.setAttribute("entryHTML", getEntryHTML(entryId));
request.setAttribute("entryTitle", getEntryTitle(entryId));

request.getRequestDispatcher(forward).forward(request, response);
} catch (Exception e) {
response.sendError(404);
}
}
};


/**
* POST a new entry to the collection URI
*/
RequestHandler collectionPostHandler = new RequestHandler(){

public void invoke(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String entryId = saveEntry(request, response);

response.setHeader("Location", buildEntryUri(entryId));
response.setStatus(201);
//write out some HTML or maybe the entry itself.
String forward = "/WEB-INF/entry.jsp";
request.setAttribute("entryHTML", getEntryHTML(entryId));
request.setAttribute("entryTitle", getEntryTitle(entryId));
request.getRequestDispatcher(forward).forward(request, response);
} catch (Exception e) {
response.sendError(404);
}
}
};

//Stubbing these in...
RequestHandler entryPutHandler =null;
RequestHandler entryDeleteHandler = null;
RequestHandler collectionGetHandler = null;

//
// One ResourceIdentifier per URI pattern.
// Each one tells us how to parse the pattern into attributes, and handlers
// for HTTP methods on the resources it identifies.
//
ResourceIdentifier entryIdentifier = new ResourceIdentifier(
"^/(\\d+)$", // URI pattern
new String[] {"entryId"}, // match pattern and insert named request attributes
entryGetHandler, // GET handler for entry URIs
null, // no POST handler for entries
entryPutHandler, // PUT an entry
entryDeleteHandler); // DELETE an entry


ResourceIdentifier collectionIdentifier = new ResourceIdentifier(
"^/$", // URI pattern
collectionGetHandler, // GET handler for collection would list entries
collectionPostHandler); // POST handler for collection will add and entry



@Override
/**
* Here's how we tell our base class how to map URIs to handlers:
*/
protected ResourceIdentifier[] resourceIdentifiers() {
return new ResourceIdentifier[]{entryIdentifier, collectionIdentifier};
}

// these are just stubs... exercise for the reader.
private String buildEntryUri(String entryId) {
return null;
}

private String saveEntry(HttpServletRequest request,
HttpServletResponse response) {
return null;
}

private Object getEntryTitle(String entryId) {
return null;
}
private Object getEntryHTML(String entryId) {
return null;
}

}




Here's the base RestServlet class. In real life this class also has convenience methods to send redirects, and other standard HTTP stuff.



public abstract class RestServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

private static Logger logger = Logger
.getLogger(RestServlet.class.getName());

protected RestServlet() {
super();
}

protected abstract ResourceIdentifier[] resourceIdentifiers();

/** try calling doGet, doPost, or whatever, on each ResourceIdentifier, until one succeeds.
Uses reflection to reduce bloat.
*/

private void doMethod(String methodName, HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
boolean did = false;
try {
// The method must be like e.g.
// boolean doGet(HttpServletRequest request, HttpServletResponse response)
Method method = ResourceIdentifier.class.getMethod(methodName, new Class[]{HttpServletRequest.class, HttpServletResponse.class});
Object [] params = new Object[]{request, response};
for (ResourceIdentifier rid : resourceIdentifiers()) {
if (did = (Boolean)method.invoke(rid, params)) {
break;
}
}
if (!did) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception in doPost", e);
throw new ServletException(e);
}

}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doMethod ("doPost", request, response);

}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doMethod ("doGet", request, response);
}

@Override
protected void doPut(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doMethod ("doPut", request, response);
}

@Override
protected void doDelete(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doMethod ("doDelete", request, response);
}



Here's the ResourceIdentifier class. It tells us how to map URI patterns to handlers, and maps matched pattern groups to attribute names

public class ResourceIdentifier {

private final Pattern pattern;
private final String[] attributeNames;
private final RequestHandler getHandler;
private final RequestHandler putHandler;
private final RequestHandler postHandler;
private final RequestHandler deleteHandler;

public ResourceIdentifier(String regex, String[] attributeNames, RequestHandler getHandler, RequestHandler postHandler,
RequestHandler putHandler, RequestHandler deleteHandler) {
this.pattern = Pattern.compile(regex);
this.attributeNames = attributeNames;
this.getHandler = getHandler;
this.postHandler = postHandler;
this.putHandler = putHandler;
this.deleteHandler = deleteHandler;
}


public ResourceIdentifier(String regex, RequestHandler supportsGet) {
this(regex, new String[] {}, supportsGet, null, null, null);
}

public ResourceIdentifier(String regex, String[] attributeNames, RequestHandler supportsGet) {
this(regex, attributeNames, supportsGet, null, null, null);
}

public ResourceIdentifier(String regex, RequestHandler supportsGet, RequestHandler supportsPost) {
this(regex, new String[] {}, supportsGet, supportsPost, null, null);
}

public ResourceIdentifier(String regex, String[] attributeNames, RequestHandler supportsGet, RequestHandler supportsPost) {
this(regex, attributeNames, supportsGet, supportsPost, null, null);
}

public boolean doGet(HttpServletRequest request, HttpServletResponse response)
throws Exception {

return doMethod(this.getHandler, request.getPathInfo(), request, response);
}

public boolean doPost(HttpServletRequest request, HttpServletResponse response)
throws Exception {

return doMethod(postHandler, request.getPathInfo(), request, response);
}

public boolean doPut (HttpServletRequest request, HttpServletResponse response)
throws Exception {

return doMethod(putHandler, request.getPathInfo(), request, response);
}

public boolean doDelete(HttpServletRequest request, HttpServletResponse response)
throws Exception {

return doMethod(deleteHandler, request.getPathInfo(), request, response);
}

/**
* Test the uri against our pattern. If matched, dispatch to the handler.
*/
private boolean doMethod(RequestHandler handler, String uri, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (uri == null){
uri = "";
}
Matcher matcher = pattern.matcher(uri);
boolean bDid;
if (matcher.matches()) {
if (handler == null) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
bDid = true;
} else {
dispatch(matcher, request, response, handler);
bDid = true;
}
} else {
bDid = false;
}
return bDid;
}


private void dispatch(Matcher matcher, HttpServletRequest request, HttpServletResponse response,
RequestHandler listener) throws Exception {

// The regex matched. Extract all the named attributes from the URL and
// set them as attributes on the request. Then invoke RequestHandler.
int n = matcher.groupCount() ;
if (n != attributeNames.length) {
throw new RuntimeException("must have same number of matches as attribute names");
}
for (int i = 0; i < n; i++) {
request.setAttribute(attributeNames[i], matcher.group(i + 1));
}
listener.invoke(request, response);
}
}


The RequestHandler interface is the merest slip of a thing:


public interface RequestHandler {
void invoke(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

Friday, June 08, 2007

Hyperbolism of the Month

...goes to (the envelope please)... Elliotte Rusty Harold!:
Java’s exception handling is the single best error handling and reporting mechanism ever built into a programming language.

I personally don't think you're going to beat the Common Lisp condition system. You can emulate Java-style exception handling, but it's so much richer to be able to restart in the context where the error occurred, without unwinding the stack.