Whats best way to package a Java Application with lots of dependencies? - java

I'm writing a java app using eclipse which references a few external jars and requires some config files to be user accessable.
What is the best way to package it up for deployment?
My understanding is that you cant put Jars inside another jar file, is this correct?
Can I keep my config files out of the jars and still reference them in the code? Or should the path to the config file be a command line argument?
Are there any third party plugins for eclipse to help make this easier? I'm using an ant build file at the moment but I'm not sure I know what I'm doing.
Is there an equivelent of the deployment projects in Visual studio, that will figure out everything you need and just make an installer? I've used install4j before, and it was powerful if no where near as automated as .Net deployment projects.
Cheers.

There is no one 'best way'. It depends on whether you are deploying a swing application, webstart, applet, library or web application. Each is different.
On point 2, you are correct. Jar files cannot contain other jar files. (Well, technically they can, its just that the inner jar file won't be on your classpath, effectively meaning that jar files do not contain jar files).
On point 3, you certainly can reference config files outside the jar file. You can typically reference a config file as a file or a resource. If you use the resource approach, it typically comes from the classpath (can be in a jar). If you use a file, then you specify the filename (not in a jar).
In general, most Java developers would use Apache Ant to achieve deployment. Its well documented, so take a look.

(1) An alternative to ant that you may wish to consider is maven.
A brief intro to maven can be found here.
For building a JAR, maven has the jar plugin, which can automate the process of ensuring all dependent jars are listed in your jar's manifest.
If you're using eclipse, then download the maven integration as well.
(2) Another alternative is to use OneJar (disclaimer: haven't tried this myself).

The answers vary depending on what kind of thing you're building.
If you're building a library, it's best to distribute your work as a jar file. It's possible to refer to your jar dependencies via the Class-path attribute in your jar manifest, although I generally think that's uncool. That attribute was designed for applets and it's used infrequently enough in libs that when this technique pulls stuff into the classpath (particularly common stuff the user might already be using), you can get unexpected version conflicts. And it's hard to track down why you're seeing them.
Publishing a jar to a Maven repo with pom info to track dependencies is an excellent choice for libraries as well. If you do that, please publish your Maven coordinates in your docs!
If you're building an app, the two popular choices are to distribute a zip/tar/whatever of a deployment structure OR to use an installer program. If the program is a server-ish kind of thing, the former is far more common. The latter is more common for clients. Generally, the installer program is just going to lay out the deployment structure and maybe do some extra tasks like installing in OS-specific locations.
To build your deployment structure (aka "kit") you'll want to create a repeatable process in whatever build system you're using. Ant has copious examples of this and Maven has the assembly plugins that can help. Generally you'll want to include a jar of your code, any dependencies, scripts to start the program, maybe a JRE, and any other resources you might need.
If you want to create an installer, there are many options both free and commercial. Some folks I know have recently had good experiences with the free IzPack but check out your options.

You should try FatJar. It's an Eclipse plugin that with just a right click at the Project can build a JAR file with all you need to run the application, including the necesary third party JAR.
We use it everyday, in conjuction with JSmooth to create the executables, to deploy our software packages to our customers, and works like a charm.

Well, if you are speaking of deployment of a standalone desktop application:
Before we switched to web start we have been creating three deployment archives, one for windows, one for mac and one for other platforms.
On windows we have successfully used the Nullsoft Scriptable Install System (known for it's usage by the older winamp versions) and its ant task, although some drawbacks are:
It is only usable on windows AFAIR
You have to do some work by hand, i.e. customizing the wizard-created script AFAIR
It can create a windows installation with start menu entries on the other hand. There also exists an eclipse plugin for integrated NSIS shell script editing.
On Mac OS X there is an ant task to create an .app file from your java files so that you can start it like a native os x application. But beware of not writing any setting to your home dir and using the the application dir instead.
For others you have to expect they are in a un*x env and deploy your app with a shell script to start the application.
In any case you may have to deploy your custom policy file to get access rights for your application.
If you want to get rid of all the packaging and stuff you should seriously consider using web start. We have saved much time since switching to it, i.e. simplified our deployment process, takes care of updates etc.
Update 2014
Use maven assembly plugin, see section "Creating an executable jar"

Ant. It's not the best thing in the world, but it's standard, it's apache, and it works.
There's some good examples on the web how to make a simple build.xml for any, and it's got some features like the 'war' task that knows how to put all the basic stuff (classes, web.xml etc) in the jar file for you.
You can also tell it to pick up other config files and jars and it will happily do it.
It's also really smart about what to compile. You give it a directory, and it finds all the java files and builds them only if their classfile is out of date, so you get some of the traditional make functionality for free without much effort.

You could look at other java projects (e.g. JMeter, SquirrelSQL, JEdit, Cernunnos, etc.). Each package their applications slightly differently, so consider your goals when you review these.

Related

Java War file deployment with a bunch of npm packages

I'm working with a dynamic web project in Eclipse and I'm planning on a Java JAX-RS RESTful back-end with a JavaScript single-page app front-end using a framework of the Angular/Durandal/Aurelia flavor. With that said, the typical way to deploy in the Java world is to bundle things up as a WAR file - which is essentially a JAR file. The trouble is, including the node_modules blows up the size of the WAR file considerably. On the other hand, I can execute 'npm install' after deployment. However, on my development machine, where I'm constantly deploying, that will take too much time. I would prefer if I can prepare the install directory on the web server with the 'npm install' modules and then deploy the WAR file on top of it. The trouble is, it seems the WAR file deployment enjoys wiping out folders if they are not contained in the WAR file.
I'm using GlassFish 4.1 application server. The ideal solution for me would be a way to 'cloak' directory in the WAR file by modifying the MANIFEST.MF file such that when it is expanded the cloaked directories are not overwritten. This would be the most parsimonious solution to my problem. However, I know of no cloaking manifest entries for JAR/WAR file manifest.
There may also be creative solutions arrived at using the 'npm link' command. Any suggestions are welcome.
Perhaps this, among other reasons, speaks to why once people gets started with npm on the client-side they start looking at node and express on the server-side. However, I'm not convinced they can't play nice together and I would like to keep the option of all the old school open source Java libraries at my disposal.
I know this question is almost two years old, but perhaps someone will still need an answer.
Put simply, you need to bundle your JavaScript. You should never be wrapping up your node_modules folder in a war, or even deploying it as-is to the server. Mainly because of exactly the issue you were having. It's... not the smallest.
In front-end development, you're expected to use a tool like webpack to gather up all your JS files into a single app.js file. This process will only take the actual files you directly require or import in your own JavaScript (and the files that those files require, etc), leaving out all the rest. Most importantly for this discussion, leaving out all your devDependencies!
Webpack will also bundle up files other than js. Importing your css files will tell webpack to also bundle those up, creating an app.css file alongside your app.js (though you will need to use an appropriate loader to tell Webpack what it means to import 'main.css').
Getting started is a fairly straightforward matter of adding a config file to your project, adding a new devDependency, and figuring out how to get your Java-based build tool to trigger the bundler. The frontend-maven-plugin, for instance, or the gradle-node-plugin.
These days, webpack and its ilk are even smarter. If your node_modules contains ES6 native modules, bundlers can perform tree-shaking on these files to only bundle the exports that are actually imported. This reduces the bundle size even more.
They can also pull out parts of the bundle into a separate file in order to create, say, a vendor.js file that contains the code for Angular, jQuery, etc. Or you can tell the bundler to treat those imports as external, meaning that they are assumed to have been included elsewhere in the web app. But this is all getting into more advanced features than you need at first. Just give webpack's getting started guide a go, see the difference it immediately makes to your war size, and go from there.
If you are using a nodejs build tool like Grunt (but probably not), then it's likely the devDependencies that's taking up so much space. If so, just copy your runtime dependencies out of node_modules.
If not: you don't have to deploy a .war; you can also deploy an 'exploded' directory. You could copy only changed files and touch .reload
Plus to mentioned above tools to pack NPM resources, let me also mention JNPM: https://github.com/OrienteerBAP/JNPM
It provides maven plugin (jnpm-maven-plugin) to download, filter and pack required NPM packages into your JAR/WAR. So in you case you should publish your client code as NPM package and then pack it into your WAR through this plugin.

Eclipse (java) web services, what files to version and how to properly pull the project to a new computer?

Alright, so I have a web service that was created using an eclipse dynamic web project. It is currently shared on a CVS repository, but the versioning system used is irrelevant. At the moment, I have literally NEVER been able to pull this project out as is and get it working. It leads to countless errors that cannot be fixed. Every time I need to work on this webservice in a new machine I have to create an entirely new dynamic project, copy over the source files, add all the necessary libraries and make the deployment assembly work correctly again. After finally making it run I share the project as the same one, stop after a second, and then synchronize again (in a way tricking eclipse into thinking this was the shared project all along).
I feel like others must have run into this problem and found a way around it. So if you have a web service or any dynamic web project, what files do you share, and how do you successfully pull it from the repository and get it to run on another machine besides what I currently do now?
Your help is much appreciated,
-Asaf
Edit: After reading some of the responses I feel that this question is actually more specific to those who use WTP to create/test their web services. Just wanted to add the clarification.
Edit2: Let me also clarify that the other 20 or so projects not using WTP are shared just fine. I am able to pull and run them with no problem. Only web service projects are an issue.
In general, you want to check in everything that's not "derived" (generated or compiled - that's usually the contents of the bin directory or other place where your code is compiled/built into). For Eclipse Java projects, you want to include the .project, .classpath, .settings, and any other similar files that Web Tools might create for Dynamic Web projects. The Eclipse CVS client will ignore files marked as Derived so you shouldn't have to worry to much about it.
Without more detail about what kind of problems you've run into, it's not possible to guess what was causing them. My only guess is that perhaps you had different versions of Eclipse and/or the WTP (Web Tools Platform) plugins installed on the different machine. That's just a wild guess, but could explain some incompatibility when you check out the project from CVS.
Bottom line, checking in those .* files is the long recommended approach from Eclipse gurus. Maven can kind of change things, but you didn't mention it so I'm assuming you aren't using it.
I am primarily sharing my experience, may be you can find some help.
Conceptually speaking, the files which the IDE can generate itself while creating new project should not be pushed. I.e the IDE specific files should not be pushed. And everything which the IDE cannot generate on its own must be pushed.
Forexample in case of eclipse, following files should not be pushed:
.settings
build
.classpath
.project
For setting the project on new machine, first pull the files from server, and then create a project from IDE using pulled files.
EDIT: If your project has external jars/libraries, then you will have to add to the classpath manually. You could also push .classpath but that might give errors while creating a new project.
I think it's easiest to use a build system and let the IDE generate the project from your build system.
Eclipse, Netbeans, and Intellij are all pretty good at building projects from maven or ant build files. With this solution you have a simple build that is easy to setup in CI (Hudson, Bamboo, whatever) and you don't have any IDE specific files checked in. If my workspace is totally different than yours, with different versions, plugins, whatever, I'm not stuck with your project file and you're not stuck with mine. My IDE creates the project appropriate for my environment and your IDE does the same for yours.
Since you mentioned having to manually add libraries, I assume you are not using any build manager (like, maven or ant) besides ecplise.
For ecplise to handle the project properly you need the source files (*.java) in their respective directories, any resources bundled with the web service (e.g. services.xml), the ".project", ".classpath", ".settings", etc. files for eclipse. This should be enough for eclipse to generate anything else necessary to build the project.
Any files/directories that are generated by eclipse during the build process (e.g. target & bin directory, *.class, *.war) should not be checked in -- they will be generated when needed during the build.
I am thinking that, since you are adding the necessary 3rd-party jars manually, these libraries might reside in a different path between computers (e.g. if the path contains the username, it will not be transferable to another computer for a different user). To fix that you can set up the classpath using an eclipse classpath variable. In Preferences->Java->Build Path->Classpath Variables set up a varable linked to the "root" folder where the 3rd party jars a stored. Then add the libraries to the project using this new variable, not their full path. To make it work on someone else's computer, you would only need to set this classpath variable to have the build path point to the correct libraries.
It might be beneficial if you migrated your project from eclipse only to a build manager (e.g. maven) that takes care of many of these issues for you. Eclipse can build a project from the configuration of the build manager, making it easier to manage the project.

How to load a resource from compound JAR?

I have the following problem:
I am writing an application that uses some of the JARs from the Netbeans Platform. To be exact, I am only using the Netbeans Visual Library for creating some graphs. This can be done without using the Netbeans Platform by extracting 3 JARs from the platform. This is all working well, except for 1 problem.
Some Background
I am using the Java Simple Plugin Framework (JSPF) to handle my plugin management. So I have an application that basically consists of a skeleton framework, and then depending on which plugin JARs it finds, it can do various things, one of which is drawing graphs. The JAR plugin for this functionality has all it's dependant libraries inside. This is done by exporting the JAR as an artifact in IntelliJ, which will unJAR all the dependant libraries and reJAR them inside yours (so everything you need is there).
The Problem
What seems to be happening though, is that when it tries to start use the classes from the embedded libraries, it works fine, but when it needs resources (.png specifically in my case), it complains that it cannot find it.
My Thoughts
The only thing I can think of why it is not working, is that it could be since the plugin JAR is not in a classpath. Could this be it?
Is there anyway to specify a classpath directory in the MANIFEST maybe? Otherwise must I create my own ClassLoader and manually load all the JARs in the plugins directory?
Thank you!
UPDATE:
I have subsequently pinpointed that it is indeed a problem with the classpath. If I place my compound library on the classpath, everything works perfectly. The problem I experience now though is:
If I copy the library to /Library/Java/Home/lib/ext/ it works fine. If I execute the application with java -cp "/path/to/plugins/myLib.jar" -jar Application.jar it does not work.
How can I load all the jars in the plugins directory into my application so the resources inside them can be used?
Thanks again!
So I have finally figured out what was happening. When creating a executable jar, the MANIFEST.MF file overrides any classpath you specify in the command-line, which basically renders it useless if you want to specify external jars. This seems to be a general problem that has been logged since Java 1.3 already.
My simple solution is to simply not create a executable jar, and then launch the application with a script:
java -cp App.jar:plugins/* my.package.structure.App
which works perfectly.
The default classloader's do not load classes in nested jars. You'll need to write your own classloader to get the classes in the nested jars.
You can check out this jspf article...
"I forgot: Adding dependencies as JARs inside JARs is not possible, because it would not work in all scenarios (e.g., applets); IIRC also tools like Eclipse would have problems if you used classes with unresolved (read: runtime-resolved-dependencies). To my knowledge there is no established way yet to gracefully handle nested JARs in all circumstances."
http://code.google.com/p/jspf/wiki/UsageGuide

How to set up a subvresion repository from my netbeans project

I was recently given some code that was worked on by someone other than myself, and after lots of work involving hunting down external dependencies, and editing the build.xml file to get ANT to build things in a sane way, I'd like to now get the code into a subversion repository.
The team I'm working on is rather small, but the members on the team change often. So I want people to be able to start working on this code as quickly as possible. A simple checkout from the repository, and opening the project in Netbeans to have everything building and executing properly would be ideal.
How do I achieve this when there are external dependencies that are not going to be on other team members machines?
Should I place the libraries my project uses in the VCS?
How do I instruct subversion to not track generated files such as class files and jar files?
Should binary resources that are unlikely to change such as images and sounds be placed in the VCS? If not what would be the best way to distribute them?
Thank you.
1) yes. Anything needed to build and deploy the application and isn't generated by the build process goes into version control (yah, I know the maven fanz don't like that).
2) make sure everything that's generated goes into distinct directories that you exclude from version control.
3) see 1). Same thing. Same with project documentation, release notes, etc. etc.
The biggest problem of course are directory names which will be set in your IDEs configuration files and differ between development machines.
Not just the location of the project directories, but the IDE itself, JDKs and other tools, appservers, can all be different between machines. It's a constant problem.
I usually use maven cause you can have a repository for jars and dependencies this makes life easier, but since you have already refactor your code to use ANT maybe this is not what you want, to use subversion with netbeans then you might want to add another folder for jars like libs or something and then another for the binaries like audio etc. Also check for the svn plugin for netbeans it will add the ignore to the dir and just update java files.
To add files to ignore you can use
svn propset svn:ignore -F .
or you can look for the config file in your subversion home and add something like this
global-ignores=*.classs
Hope this helps

Easiest way to manage my CLASSPATH?

I'm beginning to play with Clojure a bit and my Java experience is pretty limited. I'm coming from the dynamic world of Ruby and OO, so the functional side of things is very interesting!
Anyway, as I discover libraries and various tools for use (and the tutorial files for the Pragmatic Clojure Book), everything typically calls for placing files in the CLASSPATH in order for Clojure to see the library for use.
Is there such thing as good CLASSPATH practice? Would I ever want to only have a CLASSPATH with just the external libraries of files I need or can I go ahead toss any library or file I would ever need in a directory and simply define it as my CLASSPATH and only require what's needed?
If it helps, I'm an OSX and Emacs user (Using slime and swank-clojure).
I recommend using leiningen and lein-swank to manage this. You can start a REPL in the directory and connect to it from Emacs.
Personally, I'm using a variant of a clojure-project elisp function by Phil Hagelberg, see source in this post to the Clojure group. It sets up the classpath appropriately for the project you'll be working on, then launches SLIME. (EDIT: You'll need to change the value which gets assigned to swank-clojure-jar-path to point to clojure.jar. I'm using (expand-file-name "~/.clojure/clojure.jar") as the default.)
To answer the question about having everything on the classpath all the time vs only throwing in what's needed: to the best of my knowledge, nothing will actually break if you take the first approach (I know I do that for experimental purposes), but apparently things might break with the first approach (see cjstehno's comment below) and in a proper project I find the second to be cleaner. At some point it'll be necessary to determine what libs are being used (and which versions of them), if only to tell leiningen (or maven) about it -- why not keep tabs on it as you go.
We are using Clojure and use a number of infrastructure tools, especially Eclipse (IDE) (http://en.wikipedia.org/wiki/Eclipse_%28software%29) and maven (http://en.wikipedia.org/wiki/Apache_Maven). maven manages libraries and jar dependencies so if you have a number of these and they are likely to grow start using maven.
In answer to your original question you can just put your jars in one directory and you can access them by name every time you run. But you will benefit from the tools...
If you are just exploring then Eclipse will probably manage your jar files fairly painlessly. You can add these to the project as required through the Build Path -> Configure Build Path option.
As your work progresses you will possibly wish to split it into Projects which Eclipse supports so you can add your (or other projects) to your Build Path.
If you use external Clojure libraries look to see if they have been packaged as maven projects (they will have a pom.xml file). The POM will give a list of dependencies.
#
The usual CLASSPATH practice for Java is to put only the jar files needed for a project into this projects class path, which means to have potentially different class paths for diffent projects. This is usually managed by the IDE as part of it's project properties.
Since you are using Emacs and thus probably don't have or use something like projects it might be more convinient for you to set up and use a single global class path for all your clojure related stuff or maybe even simply put all the needed jar files into the java2se/jre/lib/ext directory of your java installation.
The two main problems that could arise from having unneded jar files in your class path are: 1. it has a minor negative impact on the start up time of the JVM and 2. it becomes more difficult to make sure that you are not having classes with different versions in the same class path (i.e. different classes with the same package and name in different jar files).
Since Java SE 1.6 (or JDK 1.6) you can include class path entries by wildcard. If your class files live in .\bin, and your library jar files live in .\lib, then on Windows you could define your class path like this:
set CLASSPATH=bin;lib\*;
This will let you add jar files into the .\lib directory and they will automatically be added to the class path for new instances of the JRE.
See this link for details: Setting the class path
Prior to JDK 1.6 you had to add each jar file onto the ClassPath individually.
I just discovered this bit which I need to give a shot:
(setq swank-clojure-extra-classpaths (list "/class/path/1" "/class/path/2" "/class/path/3" "etc"))
clojure-contrib/launchers/bash/clj-env-dir has an interesting property that you can point it at a directory and it will basically include anything in there. In the past I've had a ~/classpath directory which I would dump any jars into and link any commonly used directories and it worked great. Very simple way to dump and use. Now I tend to use Maven clojure-maven-plugin and that works well also though can be a bit tedious when you just want to muck around with some ideas.

Categories

Resources