Atom Feed SITE FEED   ADD TO GOOGLE READER

Always use jarjar to package implementation dependencies

jarjar is a sweet Java packaging tool that allows you to embed one .jar file in another. But rather than just smashing the jars together in one big archive, jarjar renames the embedded .jar's classes so that they live in the main jar's namespace. For example, Guice's ProxyFactory.java file has an impressive collection of imports:
package com.google.inject;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.internal.*;
import java.lang.reflect.*;
import java.util.*;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastConstructor;

class ProxyFactory implements ConstructionProxyFactory {
...
}

These imports include classes from net.sf.cglib and com.googe.common.collect. But when we package it up with jarjar, everything gets prefixed with the Guice package name: com/google/inject:
     0 Thu Jan 01 22:13:36 PST 2009 META-INF/
1726 Thu Jan 01 22:13:34 PST 2009 META-INF/MANIFEST.MF
11357 Sun May 25 20:34:04 PDT 2008 LICENSE
101 Sun May 25 20:34:04 PDT 2008 NOTICE
5975 Thu Jan 01 22:13:20 PST 2009 com/google/inject/AbstractModule.class
2466 Thu Jan 01 22:13:20 PST 2009 com/google/inject/Binder.class
806 Thu Jan 01 22:13:20 PST 2009 com/google/inject/Binding.class
414 Thu Jan 01 22:13:22 PST 2009 com/google/inject/BindingAnnotation.class
...
136 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/proxy/Callback.class
238 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/proxy/CallbackFilter.class
28315 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/proxy/Enhancer.class
5712 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/proxy/MethodProxy.class
5535 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/reflect/FastClass.class
1642 Sun May 25 20:34:04 PDT 2008 com/google/inject/internal/cglib/reflect/FastConstructor.class
...
8144 Sat Nov 29 14:11:22 PST 2008 com/google/inject/internal/collect/ImmutableList.class
9826 Sat Nov 29 14:11:26 PST 2008 com/google/inject/internal/collect/ImmutableMap.class
6026 Sat Nov 29 14:11:24 PST 2008 com/google/inject/internal/collect/Lists.class
12551 Sat Nov 29 14:11:26 PST 2008 com/google/inject/internal/collect/Maps.class

By using jarjar, Guice encapsulates these library dependencies. This is fantastic! Many problems are avoided by encapsulating the library dependency:
  • Guice users don't need to tell their classpath, IDE or build.xml files about cglib or Google Collections.

  • Other versions of the libraries won't conflict with the Guice version. We can ship one binary and support users of either extremely old or extremely new versions of cglib. Even if cglib broke compatibility every release (it doesn't), we aren't impacted.

  • We can freely change our own libraries. Should we add a dependency on paranamer in a future release, our users don't need to reconfigure their build scripts.

  • Most importantly, we can patch the libraries to fit our needs. If we want a special build of google-collections that includes our own hacks and tweaks, that's just fine. Our build doesn't even need to be compatible with the public version.


Standard jars for API dependencies


Guice's dependency on aopalliance doesn't use jarjar. Guice users implement the aopalliance interfaces, so the version independence and encapsulation offered by jarjar doesn't make much sense.