Friday, December 17, 2010

Java WebStart 'offline-allowed'

Wow... been going crazy on this one for a few days.  I have an application that is being delivered via JNLP to run under WebStart.  As a requirement, the application has to run online or offline.  When offline, the data that is to later be transmitted will sit in a local 'queue'.  Once the system comes back online, all of the data in the queue will be uploaded.

I just, for the life of me, could not get this to work!  I checked, double-checked, and tripled-checked the format of my JNLP code.  Nothing seemed to work.  Finally, I decided to look at the JNLP returned from my server (Grails controller generated), and then I saw it!  Cache control headers were being set, telling clients NOT to cache.  It turns out the Java process that downloads the JNLP (javaws) respects that cache setting!  The problem wasn't with the JNLP, but rather the HTTP headers.  Problem solved :)

Here's the trivial script I wrote using Groovy and Commons HttpClient to snoop the headers:

1:  import org.apache.commons.httpclient.HttpClient  
2:  import org.apache.commons.httpclient.methods.GetMethod  
3:  def client = new HttpClient()  
4:  def get = new GetMethod("http://localhost:8080/myapp/jnlp/test")  
5:  try {  
6:   println client.executeMethod(get)  
7:   println get.responseBodyAsStream.text  
8:   get.responseHeaders.each { println it }  
9:  } finally {  
10:   get.releaseConnection()  
11: }  

... one final thought.  If you're generating your JNLP dynamically, make sure you set a Last-Modified response header.  WebStart seems to use that then when determining if the JNLP file has changed, and should thus check for JAR updates.  My suggestion, use the most recently updated JAR that is specified in your JNLP as the value for the Last-Modified header:

def jarFile = findFromExpandedWar(servletContext)
def gmt = TimeZone.getTimeZone("GMT")
def modified =  new Date(jarFile.lastModified())
def pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"
response.setHeader("Last-Modified",  DateFormatUtils.format(modified, pattern,  gmt))

You can find the client JAR files by using ServletContext's getRealPath() method.

0 comments: