Technology

Net Neutrality in Canada

I used to have a link to Neutrality.ca on my blog. It was a great website outlining the net neutrality issues in Canada. But I just noticed that the site has been taken down due to "legal concerns". This is a real shame and I wonder what the concerns are.

Net neutrality is an issue that has received a lot of attention in the US, but sadly has been mostly ignored in Canada up until recently. If you care about your rights as a consumer or simply about free speech, you should be taking notice. For more information just search on Google. It will turn up plenty of sites that do an excellent job of explaining what is at stake.

In other news, I'll be attending BioIT World in Boston from April 30 - May 2nd. I'm going primarily to attend the lectures, but I'll also be hanging around the GenoLogics booth quite a bit. Something tells me having an IT savvy person at the booth during an IT trade show is probably a good idea. ;-)

Unsign a JAR with Ant

I was working on the build system today and came across this Ant macro I wrote a while ago. It unsigns a JAR by removing all signatures from the manifest and re-jar'ing it. This is useful if you want to deploy your application via Webstart and want to include all JARs inside one JNLP file, instead of using JNLP extensions for JARs with different signatures. Simply unsign all JARs and then re-sign them with your own signature.

Since there is no straight-forward way to do this in Ant I had to write my own macro. This might come in useful to other people, so I decided to post it:

<macrodef name="unsignjar">
	
    <attribute name="jar"/>
    	
    <sequential>
	<!-- Remove any existing signatures from a JAR file. -->
	<tempfile prefix="usignjar-" destdir="${java.io.tmpdir}" property="temp.file"/>
        <echo message="Removing signatures from JAR: @{jar}"/>
        <mkdir dir="${temp.file}"/>
	        
        <unjar src="@{jar}" dest="${temp.file}">
            <patternset>
                <include name="**"/>
                <exclude name="META-INF/*.SF"/>
                <exclude name="META-INF/*.DSA"/>
                <exclude name="META-INF/*.RSA"/>
            </patternset>
        </unjar>
	        
        <delete file="@{jar}" failonerror="true"/>
	        
        <!-- Touch it in case the file didn't have a manifest.
             Otherwise the JAR task below will fail if the manifest 
	     file doesn't exist. -->
        <mkdir dir="${temp.file}/META-INF"/>
        <touch file="${temp.file}/META-INF/MANIFEST.MF"/>
	        
        <jar destfile="@{jar}" 
            basedir="${temp.file}" 
            includes="**" 
            manifest="${temp.file}/META-INF/MANIFEST.MF"/>
	        
        <delete dir="${temp.file}" failonerror="true"/>
    </sequential>
</macrodef>

To use the macro:

  <unsignjar jar="/some/location/file.jar"/>

Embedded Tomcat Class Loading Trickery

Recently I've embedded Tomcat directly within our application. The idea is that web application extensions to the system can easily share the core of our platform and the root Spring application context. This has worked well up until yesterday when I ran into some weird class loading issues with Tomcat.

Once issue was that if an application included a JAR file that is also included in the core platform, Tomcat would still load the class from the system classloader, instead of loading it from the WAR file using the Tomcat web app classloader.

For example, this is a problem when using the Wicket web framework. Wicket will load a class for a page from inside one of the Wicket core classes using getClass().getClassLoader().loadClass(XYZ). However, since Wicket was loaded using the system class loader it cannot see the web app's classes and this will result in a ClassNotFoundException.

Another problem was using CGLib. When CGLib tried to instantiate a class using ClassLoader.defineClass() it would result in a NoClassDefFoundError. Again, the problem here is that CGLib was loaded from the system class loader and cannot resolve classes from the web app's WAR file.

It took a while to find out why this is happening. According to the Tomcat 5 Class Loader HOW-TO, it should always load classed from the web app itself before loading them from the system class loader, except for special case classes. Looking at the middle of the page however, it does say that the system class loader is used first. Unfortunately for the longest time I was looking at the Tomcat 4 Class Loader HOW-TO and on that page it still indicates that the web app class loader is always used first.

It turns out the system class loader is in fact always used first. Specifically line 1267 of WebappClassLoader in the Tomcat 5.5.17 sources. However, if you use the normal Catalina startup script it resets the system class path to only include a minimal set of classes, so in that case it would not find application specific classes using the system class loader. Therefore running Tomcat normally it would always end up using the WebappClassLoader.

To get the same behaviour when embedding Tomcat in your own application, you have to create a bootstrap class. You only include the bootstrap class in the class path when loading your application. The bootstrap class then creates a URLClassLoader to load in the rest of your application classes. For example:

public class Bootstrap
{
    public static void main(String args[])
    {
        String root = args[0];
        
        try
        {
            List<URL> classpath = new ArrayList<URL>();
            classpath.add(new File(root + File.separator + "conf" + File.separator).toURL());
            addJarFileUrls(classpath, new File(root + File.separator + "libs"));
            
            ClassLoader cl = new URLClassLoader(classpath.toArray(new URL[0]));
            
            // Set the proper classloader for this thread.
            Thread.currentThread().setContextClassLoader(cl);
            
            // Use reflection to load a class to normally load the rest of the app.
            // Reflection will use the Thread's context class loader and therefore pick up
            // the rest of our libraries.
            Class appClass = cl.loadClass("com.neatstep.frank.Application");
            Object app = appClass.newInstance();

            Method m = app.getClass().getMethod("start", new Class[0]);
            m.invoke(app, new Object[0]);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * Add JAR files found in the given directory to the list of URLs.
     * @param jarUrls the list to add URLs to
     * @param root the directory to recursively search for JAR files.
     */
    private static void addJarFileUrls(List<URL> jarUrls, File root) throws MalformedURLException
    {
        File[] children = root.listFiles();

        if (children == null)
        {
            return;
        }
        
        for (int i = 0; i < children.length; i++)
        {
            File child = children[i];
            
            if (child.isDirectory() && child.canRead())
            {
                addJarFileUrls(jarUrls, child);
            }
            else if (child.isFile() && child.canRead() && 
                     child.getName().toLowerCase().endsWith(".jar"))
            {
                jarUrls.add(child.toURL());
            }
        }
}

And in a separate class file that is included in one of the JAR files which we dynamically add to the URLClassPath above:

public class Application
{
    public void start()
    {
        // Do whatever you want.

        // Initialize embedded Tomcat.
    }
}

After adding this bootstrap class to our application, everything worked fine.

More work with Lingo + Acegi Security

I've been playing around some more with Lingo and hooking it together with Spring's Acegi Security framework.

It would be awesome if Object properties on JMS messages let you send arbitrary objects. Then it would be no problem to propagate the security context. But, of course it's not that easy.

Instead I subclassed the LingoRemoteInvocationFactory and LingoInvocation classes to create SecureLingoRemoteInvocationFactory and SecureLingoInvocation. The new invocation class propagates the client side SecurityContext's Authentication to the server. I could have also set this as an attribute on the normal RemoteInvocation class, but I thought a concrete class was nicer and avoids creating+serializing a Map.

To handle the authentication information on the server I introduced a Lingo HeaderMarshaller. It pulls out the Authentication object from the invocation and authenticates it with Acegi's authentication manager.

I also introduced a new SessionAuthenticationToken and a SessionAuthenticationCreator interface. If an implementation of that interface is provided to the header marshaller it will use it to create a new session for an authenticated client.

For response messages, if the server side authentication token is a SessionAuthenticationToken, then the header marshaller includes the session ID in response messages sent to the client. In the client side header marshaller I check for this session ID and create a new SessionAuthenticationToken based on it. This token is then used in future server communication and the server then authenticates based on a session ID.

This is working pretty well, but it seems like a lot of work to make this happen. I looked at Acegi's implementation of sessions and remember-me authentication, but unfortunately it's all based around HttpSession and Servlets.

The annoying part for Lingo was that the HeaderMarshaller interface provides a client-side method that includes the RemoteInvocation object. However, at that point a JMS ObjectMessage has already been created, so adding the Authentication as an attribute to the invocation at that point seems to be too late. At least for me I could not get it to work this way, so I had to subclass the RemoteInvocationFactory. However, I'm not sure why it shouldn't work just with the header marshaller, so I really need to look into this more.

If anyone has a better idea or some more insights (or actually reads this), please post a comment. :-)

Goodbye JBoss, hello Lingo + Spring Remoting

I've been putting in some time for a little ninja project at work. The plan is to get rid of JBoss and switch to ActiveMQ, Spring Remoting with Lingo, Spring MDPs instead of MDBs and plain old Tomcat for the web apps.

So far I've removed JBoss and updated our proprietary command execution framework to use Lingo. I haven't really tested very much at all, but so far it's at least working good enough for me to load the server and log into our rich client.

In doing so I chose Lingo since it lets me use JMS for normal remote calls and for async calls. Previously our command execution framework internally used JMS for the async case, which required special handling. With Lingo I have one solution that does both, which is nice.

The downside to Lingo is that there is no proper integration with ACEGI security. There is some information on the web that claims a proper integration exists, but all they do is pass the authenticated username to the server in a JMS header. That is of course totally unacceptable since without secured queues/topics anyone could send any username. At the very least they could send username and credentials to force a re-authentication on every remote method invocation, which would be quite inefficient. It would be nicer if instead they passed the entire SecurityContext (which is what ACEGI does for standard Spring RMI remoting) and then use that to authenticate on the server. Ideally there would be a session, just like with ACEGI and Http remoting, so that we don't have to re-authenticate all the time.

I haven't look at Lingo/ACEGI in detail, but this is something I might attempt to do in future. For now I'm just passing around the SecurityContext and handling authentication in our proprietary framework outside of Lingo.

Anyway, overall the transition away from JBoss has been pretty smooth. I've been able to delete a ton of XML configuration files and code to deal with our own JMS remoting implementation. Lingo with ActiveMQ is much more straight forward to use and configure.

Hopefully soon I can get around to plugging in standard Tomcat and porting over our webapps. Of course I also have to do some performance testing along the way. :-)

Syndicate content