We're providing a client jar for other internal apps to connect to our app's REST API. Our API depends on a few standard Jakarta libraries. Is it a best practice to include those JAR files within our client jar file? Or do you just document the dependencies and it's up to the clients to ensure they have those jars on their classpath?
You should include those jars if you're deploying the app as a standalone application. If you're deploying the source for someone to build and you provide a maven pom file then you don't necessarily need to.
You should include any jars you depend on. If you allow a client to provide standard jars, you rely on them to supply the correct version. It's a lot more painless for you both if you include the versions that you know your app works with.
The pros of bundling into a single jar are obviously:
- simplicity for the client
The cons of bundling are:
- inability to update the versions of the dependent libraries
- hiding necessary libraries can actually lead to potential conflicts in the client with those extra libraries
- complexity for you in the build process (but ways to do this pretty easily in Ant/Maven)
- some libraries with manifests cannot just be unjared and rejared - you need to actually merge the manifest information. There is Ant and Maven support for this though.
Another option is to use a single main jar (your code) with the class-path manifest attribute set to suck in the other jars. Personally I don't like this as it's really easy to miss these semi-hidden dependencies.
In the end, if you're talking just one or two jars, I'd leave them split out. If you're talking 10 or 15, you might want to consider bundling.
You should not bundle the any third party jars into your own jar as an uber jar, but it would be good to include a copy of all the jar's that are required in your distribution say in a lib directory or what ever.
The main reason for this is that your clients may be using some form of dependency management system (maven/ivy etc) and providing packages and classes that don't actually belong to your project will invalidate these schemes.
There is one alternative and that is to use something like the maven shade plugin to relocate your dependencies into your own package namespace. This of course has the down side that you will increase the code size of your library but on the up side you can almost guarantee the versions of your dependencies with out effecting any other libraries your clients may be using.
EDIT: in response to Marcus Leon comment:
Possible solutions with out bundling/relocating:
- Documentation, make sure you document your dependencies and any known conflicts with previous versions - nobody actually reads it
- Distribute you library via a dependency managed system... like a maven or ivy repo, these allow you to document in a very specific set of bounds (including upper) what your dependencies are - can still be overriden just your clients will know that they are doing it
- Add OSGi information in the MANIFEST.MF - only useful if your clients actually use OSGi
- If your dependencies have been built using maven or have version information in there manifest files you could write some sort of checking routine that scans the classpath for these and checks there versions - a bit extreme
In the end it is extremely had to actually ensure you have the dependencies you want as java is a late bound language it is possible to have your dependencies (even if bundled) overridden by somebody including a different version before yours on the classpath.
Note: I've recently spent a very bad day trying to find out why one of our apps failed to find a new version of log4j. the cause: somebody trying to be helpful had bundled it into a random completely unrelated jar.
You're expressing nervousness about very specific library version dependencies (and I can understand that, given how tightly coupled, say, Hibernate is between its different modules -- only certain versions of Hibernate-annotations work with one hibernate-core, which in turn must be used with a specific hibernate-validator).
If you know a package needs to work as part of a much larger platform (say, as a plugin to a framework which may be loading its own jars), you really need the more powerful multi-versioned class-loading of OSGI (aka JSR-291): OSGI Technology
With an OSGI-enabled framework, like Spring, Eclipse, Jonas, or Glassfish, you may specify in an XML file your specific version dependencies, and if you depend upon the bundle libfoo-1.1.4, while another plugin depends upon libfoo-2.0a, the OSGI dependency manager will ensure each loads the correct version.