Tuesday, February 27, 2007

Java Web Start – JNLP Tuning

My relevant environment background is as follows:
  • Eclipse v3.2.1
  • JDK 1.5.0_10

JNLP Tuning:
------------

It is possible to tune the JNLP files to improve performance in the loading of the jar files. For example, you can specify which jars are needed and which ones are optional. The point being that the application may not need to download all of the jars to get up and running.

There are two major components that I will list here. One is to specify which jar files need to be aggressively loaded versus ones that can be lazily loaded. The other technique is to list all of the contained packages in the related jar files so Java Web Start does not have to download all Jars just to find a class file or resource. The later will make for larger JNLP files and of course make them more complex, but could improve the initial download time for users to get up and running.

Both of these techniques should actually be combined to provide the greatest performance gains.

Regardless as to what techniques are used to improve the initial loading time of the jar files, once they are in the Java Web Start cache, the load time will be measured in seconds instead of minutes for subsequent launches of the application.

Example of lazy and eager loading
---------------------------------

A download hint of lazy or eager can be specified. Java Web Start will try to honor these hints, but if it cannot find a needed resource it may download all jars anyway until it finds what it is looking for.

<resources>
<jar href="plugins/mysql_connector_j_3.1.12.jar" download="eager" />
<jar href="plugins/org.apache.log4j_1.2.14.jar" download="lazy" />
</resources>

Example of Package and Part:
----------------------------

You can provide a hint to JWS as to what is contained within a jar by the use of the Package and Part within the JNLP.

Building upon the example under lazy and eager loading:

<resources>

<jar href="plugins/mysql_connector_j_3.1.12.jar" download="eager" part="mysqlcj" />

<jar href="plugins/org.apache.log4j_1.2.14.jar" download="lazy" part="log4j" />

<package part="mysqlcj" name="com.mysqlj.*" recursive="true" />

<package part="log4j" name="org.apache.log4j.*" recursive="true" />

</resources>

Part ties the package tags back to the jar tags.

The use of the recursive="true" attribute really can save a great deal of typing. Take for example the mySQL connector j example right above. It used a value of true on the recursive attribute, but if it were to use false, you would have to list out all of the possible packages contained in the jar even though they all began with com.mysql.

<package part="mysqlcj" name="com.mysqlj.jdbc.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.configs.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.integration.c3p0.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.integration.jboss.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.jdbc2.optional.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.log.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.profiler.*" recursive="false" />

<package part="mysqlcj" name="com.mysqlj.jdbc.util.*" recursive="false" />

References:
-----------

http://lopica.sourceforge.net/ref.html#package

http://lopica.sourceforge.net/ref.html#jar

Java Web Start and Eclipse – Feature Export and an Eclipse Bug

My relevant environment background is as follows:
  • Eclipse v3.2.1
  • JDK 1.5.0_10

Eclipse 3.2 Bug with Exporting a Feature to Java Web Start
----------------------------------------------------------

There is a known bug in Eclipse version 3 that generates the wrong name for an Eclipse plugin when exporting a Deployable Feature.

The bug is marked as fixed and has also been marked as being incorporated in the main stream releases of various versions of Eclipse, such as v3.2.1. But the bug still remains.

The symptoms of this bug is the following stack dump when attempted to run under Java Web Start:

!SESSION Tue Feb 06 10:48:01 EST 2007 ------------------------------------------
!ENTRY org.eclipse.core.launcher 4 0 2007-02-06 10:48:01.112
!MESSAGE Exception launching the Eclipse Platform:
!STACK
java.lang.NullPointerException
at java.util.Hashtable.put(Unknown Source)
at org.eclipse.core.launcher.WebStartMain.basicRun(WebStartMain.java:58)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.WebStartMain.main(WebStartMain.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javaws.Launcher.executeApplication(Unknown Source)
at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
at com.sun.javaws.Launcher.continueLaunch(Unknown Source)
at com.sun.javaws.Launcher.handleApplicationDesc(Unknown Source)
at com.sun.javaws.Launcher.handleLaunchFile(Unknown Source)
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

The cause is that Eclipse is generating the plugin with a name of org.eclipse.osgi_3.2.2.R32x_v20061101.jar when it really should be org.eclipse.osgi_3.2.2-v20061101.jar.

Depending upon your version of Eclipse, you may have different versions of this jar file. All that is needed to be done is to remove the .R32x_ and replace it with -.

You must also modify the generated JNLP file to reflect the new name.

This will occur each time you perform an export of a Deployable Feature.

The reason why this is an issue, is that internally to the plugin, (or other plugins that are trying to find this one), it is using a different name/signature that does not match the use of .R32X_ .

References:
-----------

https://bugs.eclipse.org/bugs/show_bug.cgi?id=125867

http://eclipsezone.com/eclipse/forums/t64274.html

Java Web Start and Eclipse - Signing Jars and Hibernate

As a quick note:

If you are deploying an Eclipse Plugin as a Java Web Start, there are some changes that must be made to your application to ensure it plays well with everything.

This is the first of a few quick notes on the subject. I am starting with this issue, since it was the most difficult to find a resolution for.

My environment background is as follows:

  • Eclipse v3.2.1
  • hibernate plugin : com.hibernate.eclipse_3.2.0.beta9a
  • JDK 1.5.0_10

Java Web Start is basically a tool that Sun provides that allows users to run Java applications that have been bundled in Jars on their computers. It has limitations similar to those imposed upon Java Applets, but if the jars are signed, then the Java Security Manager can grant full access to all resources on the local computer. It may take a while to download numerous signed jars and verify their signatures, but once they are downloaded, rerunning the application the start time can be measured in a matter of seconds instead of minutes. JWS also provides nice features for updates and utilization of different JDK versions.

Jar Files MUST be Signed
------------------------

All jar files that are being deployed must be signed if your application must access resources outside of the sandbox. If a jar file is to be expanded on the client's computer, all nested jars must also be signed.

Java, at least 1.5 and lower, cannot deal with nested jar files. When such a situation occurs, as with the Hibernate plugin for Eclipse, Xerces, or even the Eclipse core plugin for example, the jar file must be expanded before the resources can be accessed. When the contents of a jar file is expanded, the resources it contains is no longer digitally signed (if it was decompressed, it will not match the digital signature). This is an issue for the nested jars, since they generally must be signed.

The only exception to this rule is with the Eclipse Hibernate plugin. The two jar files that cannot be signed are: hibernate3.jar and cglib-2.1.3.jar. If these two jars are signed, the following exception will result:

java.lang.SecurityException: class "com.mypackage.MyClass$$EnhancerByCGLIB$$a1a0f853"'s

signer information does not match signer information of other classes in the same package

The key part of the above error message being "signer information does not match signer information of other classes in the same package".

A side note:
------------

I am unable to find the reference that states all nested jars must be signed (it is also a low priority to find it). Signing nested jars files works with the exception above. Upon searching for the problem as listed with the ‘signer information does not match’ exception, I have found that many other people have also signed the nested jars before exporting the plugins out of Eclipse.

To me this just sounds wrong on a few levels. I have not had the chance to really test this theory by unsigning these plugins, but it will solve that question. If it does work without signing the nested jars, then it does make sense that when JWS initially expands the jars, it keeps track of the original signature and that all expanded resources has having passed the verification.

If you have references that point to the fact that nested jars do not need to be signed, please let me know. I will probably run some tests in the next week or two to verify this.

References:
-----------

http://groups.google.co.za/group/CTJUG-Forum/tree/browse_frm/month/2006-05?_done=%2Fgroup%2FCTJUG-Forum%2Fbrowse_frm%2Fmonth%2F2006-05%3F&

States that "Never sign hibernate???.jar and cglib???.jar. They seem to work just fine as they are."

http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/development.html#security

http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/contents.html