I've been trying to come up with a clean way to include and display in a webpage build information from an EAR (containing the web app war).
My first instinct is to put the bulls metadata in the ear META-INF/MANIFEST.MF file. But the chance I have with that is that the ear's manifest is not part of the classpath and consequently there is no way to read it without using file operations - generally a no no in a JEE container.
Another option is to add the bulls info to the war manifest, but that isn't giving me the necessary info on the EAR.
Lastly, I could have the build process create a properties file in a jar in the EAR/lib folder, but that feels very wrong as well... Creating a lib on the fly like that screams poor practice.
Is there a clean/property way to do this?
In Java EE 6 and newer you can place <env-entry> elements in the EAR's application.xml file.
Therefore you can place whatever additional metadata you need into one or more <env-entry> sections and then load the values either via #Resource injection or good old fashioned JNDI lookups.
Related
I have an executable jar with a war file in it. Running the jar extracts the war file and creates a WebAppContext from it using webAppContext.setWar(warFile). Although that works, it seems that webAppContext.setWarResource(warResource) should work. I've tried it creating a resource using new PathResource("file.war") which shows a path like "jar:file:/Users/.../jetty-1.0.0-jar-with-dependencies.jar!/file.war". Sounds promising and conventional, but when I try it I get "java.lang.IllegalArgumentException: not file: scheme". Do I really have to extract the war file or is there a trick?
That would be a nested jar content reference and no Java program can do that.
Option 1: use a live-war (aka an executable-war) instead.
This would be a war file that can be deployed traditionally if you want to, but can also be used standalone from the java command line (and will start it's own server if it needs to).
An example project is maintained by the Eclipse Jetty project at ...
https://github.com/jetty-project/embedded-jetty-live-war
Note: the live-war concept was inspired by work done by the Jenkins project and their live-war.
Option 2: eliminate the WAR file layer entirely in your JAR
Don't package the WAR contents in your JAR as a filename.war, consider using it as an exploded WAR (or war directory) instead.
Just unpack the WAR into your JAR file somewhere safe (like /META-INF/webapps/<app-id>/) and then reference that directory location in your JAR file instead.
Option 3: eliminate the need for the WAR concepts entirely
This is the number one most popular choice.
You deconstruct your WAR file into a ServletContextHandler with configured Servlets and Filters, this also eliminates the need for things like annotation scanning / bytecode scanning (which is quite complicated), you'll also not have to wrangle the nested / isolate classloader (your uber JAR file contains all of the classes and downstream dependencies needed to run your webapp), and this approach will definitely speed up your startup time.
I have a web project that has a \META-INF\services\javax.servlet.ServletContainerInitializer file with its content pointing to the fully qualified name of a class that implements the ServletContainerInitializer interface. I basically followed the example given here: http://nullhaus.com/2011/03/using-servlets-3-0-servletcontainerinitializer/
I put debug lines in my class that implements the ServletContainerInitializer interface and it never makes it there. Not even the default constructor...
My application folder structure is as follows:
\MyApp
\META-INF\services\javax.servlet.ServletContainerInitializer
\WEB-INF\classes\
... [list of classes and packages go here]
Any ideas what I need to check for??
Note 1: My Tomcat publishes from an exploded external folder that contains my application
Note 2: I started my Tomcat from Eclipse - if that makes a difference!
Well, I think that you'll need to wrap your initializer class (and it's services-related META-INF directory) into a separate *.jar and put it in the WEB-INF/lib.
This is a JAR service, so I guess it could have something to do with problems with discovering services in a *.war file. Moreover, it doesn't even help if you put your META-INF directory inside WEB-INF/classes and set unpackWAR=false in your Tomcat's server.xml.
HTH.
The first thing to check is that you are actually using Servlet 3.0 and not an earlier version. For Tomcat, this means that you must be using Tomcat 7.0.22
Second, make sure that the \META-INF\services\javax.servlet.ServletContainerInitializer file actually exists in the exploded war file.
Third, when in doubt, configure and start Tomcat directly (not from Eclipse) - I've seen developers have endless problems with configuration of Tomcat using the Eclipse plugin.
For tomcat to load the META-INF directory , it has to be in classes folder . If you are using maven project , just put the META-INF directory inside src/main/resources directory .. on mvn package the same will be copied to classes directory .. No need of separerate jar .. if jar is prefered you can use
HandlesTypes annotation ..
I would like to quote some good explanation from Mark Thomas <markt#apache.org> given on the user mailing list of Tomcat:
Service files are loaded by class loaders from the META-INF/services
directory.
*.jar!/META-INF/services
and
*.war/WEB-INF/classes/META-INF/services
are visible to class loaders
*.war!/META-INF/services
is not.
The servlet expert group recently discussed WAR vs JAR in the context of
Java 9 and mutli-version JARs. The conclusion was (I'm paraphrasing)
that WARs are not a specialised form of JAR and while they share a
common format a feature that is available to a JAR is NOT automatically
available to a WAR unless the Servlet spec (of Java EE spec) explicitly
states otherwise.
Containers are free to add container specific extensions if they wish
but they come with the usual (lack of) interoperability warnings.
http://mail-archives.apache.org/mod_mbox/tomcat-users/201808.mbox/
Some projects, as part of their build process, clean up the classes folder in WEB-INF, while others keep some of their config files in the classes folder. The latter group might use a temporary folder somewhere else to build their project and then copy the generated class files from this temporary folder to the WEB-INF/classes, thus keeping their config files safe.
Is there any best practice regarding web application builds about this? I use ant.
User-accessible config files are better stored outside of /classes, e.g. in /WEB-INF/config. I differentiate config files that site admins can touch (they are placed under WEB-INF) and those, kind of 'static' ones, that are meant for developers/deployment configurators only (stuff like sql scripts, XML/XSLT templates, i18n etc).
It is preferred practice to clean up WEB-INF/classes during builds - some classes get deleted/renamed, so are resource files.
Other config files, under WEB-INF, but not in /classes or /lib, have to be treated as upgradable resources: either replace old ones only when there is a new one, or use specifically designed upgrade classes to add missing config tags or lines.
There are tricky situations, like log4j.properties is sometimes stored into /classes root. It is a bit of a different story how to properly handle it, but in most instances it falls under "delete all classes and copy everything anew".
I am developing a framework that needs a lot of stuff to get working. I have several folders inside of my Eclipse project that are needed
[root]
- config
- src
- lib
- serialized
Also there are important files like the log4j.properties and the META-INF dir inside the src directory.
I wonder if there is a way to distribute one JAR containing all essential files so my gui will just have to import one jar. I guess that I have to exclude the config folder in order to make the framework configurable.
I also wonder, if there is a way to move for example the log4j.properties to the config dir so that I have one config folder containg all needed configurations?
Thanks for help and advise on this matter!
Marco
Yes, but not really. You can take all your dependencies, unpack them and simply merge them into a bigger jar. This is what the maven jar plugin does if you make a jar with dependencies. The only problem is that this might result in conflicting files (suppose two of your dependencies contain a log4j.properties). This is one of the problems when doing the above with some of the spring libraries for instance.
I think someone actually wrote a classloader that allows you to bundle the whole jar inside of your jar and use it as is. I'm not sure how mature that is though and can't at the moment recall the name.
I think you're better off distributing all your dependencies separately. Setting up the classpath is a bit of a pain but surely java programmers are used to it by now. You can add dependencies to the Class-Path header in your manifest file, in simple cases. Bigger libraries have to rely on the classpath being set up for them though.
As to the second part of your question, probably dropping the conf/ directory under META-INF is enough for its contents to be picked up. I'm not sure about this. I'm fairly sure it will always be picked up if you put its contents at the top level of the jar. In any case, this is a distribution problem. You can easily have a conf/ directory inside your source tree and have your build scripts (whatever you might be using) copy the files in it to wherever is most convenient.
As to your users configuring. Try to establish some conventions so they have to configure as little as possible. For things that must be configured, it's best to have a basic default configuration and then allow the user to override and add options through his/her own configuration file.
In terms of the resources, it is possible except that if you do that you are not going to be able to load resources (non class files) from the filesystem (via a file path).
It's likely that you're currently loading these resources from the file system. Once in the jar you need to load them as class path resources via the class.getResourceAsStream or similar.
As for the dependent jars you may have, it's common practice for these to be placed as extra jars on the classpath. I know it's complicates things but developers are used to doing this. The nature of the java landscape is that this is inevitable. What the spring framework for example does is supply a bundled zip file with the core jar and the jar dependencies included.
Is your library going to be used in an EE context or an SE context? If it is an EE context then you really don't have to worry about configuration and class path issues as the container takes care of that. In an SE context it is a lot more tricky as that work has to be done manually.
I basically want to be able to deploy multiple versions of the same EAR file to the same server (Glassfish instance?) , and have a unique path to each version separating them.
From my reading on this it appears that multiple EARs deploy to the root of the web server namespace so that they can coexist if they do not have colliding context-root's of WAR's.
In my case I'd rather have that instead of everything going under "/", I'd like to be able to brand a given EAR-file build to ALWAYS deploy under a given path like "/foo-20100319" or "/foo-CUSTOMER-20010101". This can easily be done with a single WAR file just by renaming it. I do not need or want them to disturb each other.
It is my understanding that this remapping is outside the scope of the application.xml file, so I found that http://docs.sun.com/app/docs/doc/820-7693/beayr?a=view says that I can specify web-uri and context-root, but I am not certain that what I wish to do, can be specified with these in Glassfish.
How should I approach this? I have full control over the build process.
(I have found Deploying multiple Java web apps to Glassfish in one go but I am not certain how to apply this to what I need).
The application.xml allows you to map a web app that is enclosed in an ear to the context root of your choice. You can also do this with the sun-application.xml.
Since you have full control over the build process, the choice is yours.
You may want to read about the --deploymentplan option of the deploy subcommand of GlassFish's asadmin utility. It allows you to mix additional files into the deployed archive before deployment processing... So, you can create a single 'generic' ear file and a number of shorter deploymentplan files, that 'mix-in' the sun-application.xml file necessary to create a customized deployment.