Application server-agnostic build script - java

I have a J2EE application that needs to be distributed to customers, who will have application servers of their own choice. What is the recommended way of writing a build script that would compile the J2EE application sources (at customer sites) so that they will eventually be able to deploy the application on their servers ?
Moreover, what JARs should one actually use during the compilation process ? Since this is meant to an application server agnostic script, is it recommended to use the JARs (say servlet.jar, jms.jar, ejb.jar etc.) from the application server, rather than having my customers download these JARs from a particular repository ? Is there any such repository that is recommended for such JARs (JARs of J2EE APIs) ?
PS: The EAR file cannot be built locally and shipped to customers, since additional development will be done at each installation to integrate with other systems, perform customizations on the application etc.

Build the application locally, and distribute the WAR/EAR files to the customer.
Edit:
The situation you describe is basically that you need to provide a code base for further development by the customer. In that case I would suggest that you look into creating maven modules of your work which are pushed to a private repository accessible by the customer. You then let them know that they need to depend on module X version Y in their pom.xml, and module X then pulls in all the libraries needed in the correct versions.
Note that if they don't use Maven, they might use Ant Ivy for the dependency resolving.

Some generic J2EE API jars can be found at Sun's site, however you might have to dig a lot to find them.
I would just use any JAR you have handy, bundle it with the source, but away from /WEB-INF/lib

Basically, don't include the container jars (servlet.jar, jms.jar, ejb.jar etc.) in your EAR or WAR file.
If you are just talking about servlet.jar, the web container provides that - you shouldn't include it in your war file.
If you are creating an EAR file, they you must be using an enterprise container and it will have a JMS.jar and EJB.jar.
JMS on Tomcat would be a little trickier - you'd need people to install something like activemq on their servers.

Related

Exclude lib/ext From Jetty Classpath For Specific Web Apps

I'm currently in the process of creating a web service to be deployed onto an existing Jetty 9 server. I decided to use maven to handle the dependency management for this application and it is creating a bit of a problem. When I try to start the app, it is failing due to library conflicts. I figured out that the source of the problem is that the classpath is including both my maven dependencies and the jars in $JETTY_HOME/lib/ext/. I can't simply rename or delete lib/ext since there are existing applications on the server that depend on those libraries.
Is there a way to configure jetty to include or exclude lib/ext from the classpath on a per-application basis? If not, is there some other way I can work around this problem?
TL;DR - I don't think there is a simple answer.
According to the documentation, the jars in the lib/ext directory are added to the classpath by the "ext" module. So, you could in theory disable the "ext" module. But that will remove the jars from the classpath for all of the web services in the container. So that won't work ... except for testing purposes.
So that leaves you with a couple of more drastic solutions:
Set up a separate Jetty server to run the new webapp. (For example, using Docker containers.)
Analyze the other webapps to determine which of them use the JARs in the lib/ext directory, and rebuild their WARs so that (at least) the conflicting dependencies are in the WARs that need them rather than in lib/ext.
This is probably what you should do.
Go on a Jetty code hacking spree and modify the way that Jetty builds the "server" classpath. Anything is possible, if you are prepared to put in enough coding effort. But you will be creating "technical dept" for yourself by (in effect) creating a private fork of Jetty. So this is a really bad idea.

java maven how to create app with bundled tomcat

I wonder if this is a somewhat awkward way of thinking, but I couldn't really find any hint on the internet to my idea. Maybe I just did not phrase my question right, but anyhow, this is what I would like to do:
I have a complex application written in java with spring and quartz and a whole load of dependencies. The application is run inside an apache tomcat servlet container. Now I know, I can create a war file and deploy that to the productive server machine (after our internal IT has installed and configured the tomcat on that machine), but I would like to do this a bit different.
I would like maven to create a pre-packaged tomcat application server with all dependencies and configuration settings AND my application. In effect, all that would need to be done on the productive system is, copy the package (or zip or tar.gz or whatever is needed) to the server, unpack it in a directory of my or their choice and fire up this local isolated tomcat. It would only run my application (which poses enough load on the machine anyway) and I could even go so far and deploy a second variant, say for a different customer in the directory next to the first one. Neither of both could interfere with each other, even if they use different versions with different dependencies.
Is it possible to do that? Is it a desirable approach or am I on the completely wrong track here?
What I think would be a benefit of this approach (despite the thing with incompatible dependencies or settings between two or more different installations) is, that I can hand the whole package over to our administration guys and they can simply deploy it to a server without the need to configure anything in the tomcat after installing it and so on.
Any hint???
Create a Maven project as the parent project (type pom). Include your webapp as a module project (type war). Create another module project, maybe "myapp-standalone" (type jar) and include the Embeddable Tomcat as a dependency. Write a starter class to launch the internal Tomcat (see executable jar / überjar). When building the app, copy the created war file into the jar, into Tomcats webapp directoy.
Your launcher class needs to make sure, that the ports of the current Tomcat are not yet in use.

How do I incrementally patch a war file

I am having war file deployment at customer site. War file contains lib folder which contains dependent jars e.g.
/lib/app-01.jar
/lib/spring-2.5.1.jar
/lib/somefile-1.2.jar
...
...
If we need to update lets say app-01.jar to app-02.jar, is there any elegant solution? how does these dependent jars are packaged into WAR file as industry standard?
Is it good idea to package those dependent jars without version number?
e.g.
/lib/app.jar
/lib/spring.jar
/lib/somefile.jar
...
...
EDIT NOTE:
Actually, War is deployed to Webshpere, WebLogic, Tomcat on Windows or Linux platform. And Customer's IT department is involved for deployment
Probably the most elegant solution is just to generate a new war, and deploy it.
Here are the reasons:
If you are worried about uptime, some application servers supports side by side deployment. It means that you can deploy a new version and have it up at the same time of the old one. And stop the old when no one is using it. (I've used that on WebLogic, like 5yrs ago, so I suppose that is a common feature now). But that kind of feature only works if you deploy a new .WAR version.
Probably the WAR was generated using Maven, Ant or Gradle, so changing the dependency version and do a mvn package is usually faster and less error prone than unzipping the WAR, change it, and zip it again.
All the application servers provides a "hot replace" feature, that works by refreshing the class loader. Its fine for development, but in production it can be problematic (class loader leaks can be common, and problems caused by incorrect initialization or bad programming practices can give you unexpected bugs like having two versions of a class)
About JAR file names: I recommend to keep the versions on the file name.
Most of the JARs contains version information inside META-INF/Manifest.mf. But if for some reason you have to know which versions are using your app... opening each JAR file to see the version in the manifest is a lot of work.
As a final advice. If you don't use any automatic build tool... adopt one (take a look to Gradle, which is nice). Updating a library version, usually consist on changing the version number on the build file and execute something like gradle deploy. Even if you are not the developer, but the one in charge of devops, having an automated build is going to help you with deployments and updates.
In Tomcat I don't think the war is relevant after the files have been uncompressed. You could just ignore the war and extract the new/changed files into the correct webapp's directory (The one with the same name as the war.)

GWT web-app vs system classpath for dynamic loaded classes

In my GWT web app I am keeping all my jar files outside of my project and referencing them using classpath variables. This allows me to link to jars from other projects/teams without having to put a copy of the jar in my web app lib directory. Hosted mode kindly looks up the classes in this system classpath and then adds them to the web-app classpath warning me that it is doing so. When I deploy my build system pulls in only the jars I need to ship in my web app and is not a problem.
The issue I have is that some code uses dynamic lookups, searching the classpath for implementations. If a jar has not yet been added to the web app classpath beacuse no classes have yet benn loaded from the jar it is not included in the search.
The particular problem I am having is with Persistence - it looks up EntityManagerFactory implementations by searching for META-INF/services files. I have also had a similar problem with Rome and its module extensions.
I have got a workaround, in the dev/hosted mode I simply refer to a class I know is in a jar I want and this causes it to be added to my web app classpath. I do this my by calling
private void devModeClassPathHack() {
Class<?> gwtDevModeHack1 = EntityManagerImpl.class;
}
from my development mode Guice module.
My question is simple - is there a "nicer" way of doing this?
I don't think, there's a nicer way - using this makeshift "self-healing" mechanism of the GWT Jetty/dev mode is already a hack :) If you ever have the need to run the server side code on e. g. JBoss during development (together with GWT dev mode for the client side), this will stop working.
(As an additional problem, the order of classloading - and consequently the precedence in cases when there are multiple versions of a class in different jars - will depend on program flow. This can be very nasty to debug.)
You mention, that you pull in all the jars from outside. What I would do in that case, is to set .svnignore (or .gitignore/...) to "*.jar" in the WEB-INF/lib dir. Then I'd run the same build step which is used to create the production build to copy the jars into the lib dir before starting the server.

Maven - deploying large war files

This question is somewhat similar to this one Best way to deploy large *.war to tomcat so it's a good read first, but keep on reading my q, it's different at the end...
Using maven 2 my war files are awfully large (60M). I deploy them to a set of tomcat servers and just copying the files takes too long (it's about 1m per war).
On top of that I added an RPM layer that'll package the war in an RPM file (using maven's rpm plugin). When the RPM is executed on the target machine it'll cleanup, "install" the war (just copy it), stop and start the tomcat (that's how we do things here, no hot deploys) and set up a proper context file in place. This all works fine.
The problem, however, is that the RPM files are too large and slow to copy. What take almost eh entire space is naturally the war file.
I haven't seen any off-the-shelf solution so I'm thinking of implementing one myself so I'll describe it below and this description will hopefully help explain the problem domain. I'll be happy to hear your thought on the planned solution, and better yet point me at other existing solutions and random tips.
The war files contain:
Application jars
3rd party jars
resources (property files and other resources)
WEB-INF files such as JSPs, web.xml, struts.xml etc
Most of the space is taken by #2, the 3rd party jars.
The 3rd party jars are also installed on an internal nexus server we have in the company so I can take advantage of that.
You probably guessed that by now, so the plan is to create thin wars that'll include only the application jars (the ones authored by my company), resources and WEB-INF stuff and add smartness to the RPM install script that'll copy the 3rd party jars when needed.
RPM allows you to run arbitrary scripts before or after installation so the plan is to use mvn write a list of 3rd party dependencies when building the war and add it as a resource to the RPM and then when installing an RPM the RPM installation script will run over the list of required 3rd party jars and download the new jars from nexus only if they don't exist yet.
The RPM will have to delete jars if they are not used.
The RPM will also have to either rebuild the war for tomcat to explode it or add the 3rd party jars to common/lib or something like that although we have a few web-apps per tomcat so it'll make things complicated in that sense. Maybe explode the jar by itself and then copy the 3rd party jars to WEB-INF/lib
Your input is appreciated :)
We have a directory on the target machines with all third party jars we're using (about 110Mb). The jars are using a naming coding convention that includes their version number (asm-3.2.jar, asm-2.2.3.jar ...). When adding a new version of a third party we don't delete the older version.
When deploying, our jar files contains only business logic classes and resources we compile in the build (no third party). The classpath is defined in the jar manifest where we cherry pick which third party it should be using at runtime. We're doing that with ant, no maven involved and we have more then 25 types of services in our system (very "soa" though I dislike this over buzzed word). That business logic jar is the only jar in the jvm classpath when starting the process and it is also versioned by our code repo revision number. If you go back to older revision (rollback) of our code that might be using an older third party jar its still going to work as we don't remove old jars. New third party jars should be propagated to production machines before the business code that uses them does. But once they're there they're not going to be re-pushed on each deployment.
Overall we lean towards simplicity (i.e. not OSGi) and we don't use Maven.
I would advise against your proposed plan. It sounds like a lot of moving pieces that are likely hard to test and/or diagnose problem when they arise.
We don't have the problem of "large" WARs but we do have the problem that most of our WARs all need the exact same 3-rd party libraries on their classpath. The solution we went forth with (that has worked very well) was to utilize OSGi to build our application modularly. We use Felix as our OSGi container which runs inside of Tomcat. We then deploy all of our dependencies/libraries to Felix once. Then we deploy "thin" WARs which just reference OSGi dependencies by Importing the packages it needs from the bundles it cares about.
This has a few other advantages:
Deploying new versions of OSGi bundles while the old ones are running is not an issue which allows for no downtime (similar to hot deploy).
If you need to upgrade one of your dependencies (e.g. Spring 2.5 -> 3.0), you only need to upgrade the Spring bundle running in OSGi; no need to deliver (or package) new WARs if the APIs did not change. This can all (once again) be done on a live running OSGi container, no need to turn anything off.
OSGI guarantees your bundles do not share classpaths. This helps keep your code cleaner because each WAR only needs knowledge of what it cares about.
Setting up your WARs to be "OSGi ready" is not trivial but it is well documented. Try checking out How to get started with OSGi or just Google for 3rd party tutorials. Trust me, the initial investment will save you much time and many headaches in the future.
It is probably best not to re-invent the modularity wheel if possible.

Categories