A day ago my application was one EAR, containing one WAR, one EJB JAR, and a couple of utility JAR files. I had a POJO singleton class in one of those utility files, it worked, and all was well with the world:
EAR
|--- WAR
|--- EJB JAR
|--- Util 1 JAR
|--- Util 2 JAR
|--- etc.
Then I created a second WAR and found out (the hard way) that each WAR has its own ClassLoader, so each WAR sees a different singleton, and things break down from there. This is not so good.
EAR
|--- WAR 1
|--- WAR 2
|--- EJB JAR
|--- Util 1 JAR
|--- Util 2 JAR
|--- etc.
So, I'm looking for a way to create a Java singleton object that will work across WARs (across ClassLoaders?). The #Singleton EJB annotation seemed pretty promising until I found that JBoss 5.1 doesn't seem to support that annotation (which was added as part of EJB 3.1). Did I miss something - can I use #Singleton with JBoss 5.1? Upgrading to JBoss AS 6 is not an option right now.
Alternately, I'd be just as happy to not have to use EJB to implement my singleton. What else can I do to solve this problem? Basically, I need a semi-application-wide* hook into a whole bunch of other objects, like various cached data, and app config info. As a last resort, I've already considered merging my two WARs into one, but that would be pretty hellish.
*Meaning: available basically anywhere above a certain layer; for now, mostly in my WARs - the View and Controller (in a loose sense).
Edit: I should really be calling it Java EE rather than J2EE, shouldn't I?
Edit 2: Many thanks again to #Yishai for all the help. After some trial-and-error it looks like I've figured out how to use a single ClassLoader across WARs under JBoss 5. I'm detailing this below for my own sake, and hopefully others will find this useful as well.
N.B. this is rather different from doing this under JBoss 4 (see Yishai's answer or my links below).
Instead of writing a jboss-web.xml for each WAR, and a jboss.xml for ear EJB-JAR, put a jboss-classloading.xml file in each WAR, in the same location as the DD (web.xml). The contents of jboss-classloading.xml should be:
<?xml version="1.0" encoding="UTF-8"?>
<classloading
xmlns="urn:jboss:classloading:1.0"
name="mywar.war"
domain="DefaultDomain"
parent-domain="Ignored"
export-all="NON_EMPTY"
import-all="true">
</classloading>
This follows from the JBoss CW here, whereas what (I think) works for JBoss 4.x is described here. More general info on JBoss classload(ing/ers):
Overview
History
Use cases
As best I can tell, the JBoss community wiki docs are pretty lacking for JBoss 5 in comparison to JBoss 4.
Although the EJB3.1 spec introduces singleton and your version of JBoss doesn't support it, you can use the JBoss #Service annotation to create a singleton. Instructions here. Also, it seems that you have JBoss configured to isolate your ejb jars and wars from each other. You don't have to do that. You can look at the loader-repository tag in the jboss specific xml files so that your whole ear shares one classloader (or perhaps that at least the two wars share one classloader).
All that being said, I agree with #duffymo, that a singleton which shares state between the two wars is an idea that you should be walking, if not running away from.
Edit: Regarding singletons, I suggest you look at questions like this one (which also has some nice balance in the comments).
The idea of having an object hold cached state in and of itself is ok, especially with EJB3 where you can inject your state instead of statically referencing it (if you use the #Service annotation, then you want the #Depends JBoss specific annotation). That being said, if you were using a "proper" singleton here, then I would expect that your only problem with the fact that your WARs have two separate classloaders is the extra memory footprint. Otherwise you are into the problematic area of singletons (where they have to be initialized to be used, everything that uses them has to ensure they are initialized first, and of course all code gets highly coupled with their being initialized).
Where Singletons are really really bad is where they store state so that one class can change state and another class picks it up. It is basically a no-no in EJBs until 3.1, and even then it makes a lot of concurrency issues.
Edit (further): So you want to go with the classloader repository. I use JBoss 4.2.3, so I don't necessarily know all of the ins and outs of JBoss5 (which did rewrite its classloader although they say it is almost fully backwards compatable), however in 4.2.x by default your configuration causes no problems because all the ears deployed on the server share the same classloader (the "unified classloader"). What I suspect is that the server you are deploying to has the configuration differently, so I'm not quote sure how to interact with it, but what you have to do is add a file called jboss-app.xml in your ear (in the same location as the application.xml) that looks something like this:
<?xml version="1.0"?>
<!DOCTYPE jboss-app PUBLIC "-//JBoss//DTD J2EE Application 4.2//EN"
"http://www.jboss.org/j2ee/dtd/jboss-app_4_2.dtd">
<jboss-app>
<loader-repository>
com.yourcomany:archive=yourear
</loader-repository>
</jboss-app>
That is for JBoss 4.2. 5.1 has the same type of tag, here is the xsd. It has the same loader-repository concept.
That should be it. That is, as long as your ejb-jar, war, etc. don't have it, then they don't need it. However, your wars (in jboss-web.xml - same location as the web.xml) may need the same thing. In this case as long as you name the repository exactly the same way (if I understand correctly - never tried it myself) they will share the same classloader. The same goes for the EJB as configured in the jboss.xml that goes in the same location as the ejb.xml.
This might make it a bit clearer.
I'd configure a separate object pool on your app server so it only contained the single instance.
Why you would want to do this is the real question. Sounds like all your apps will be coupled this way. And Google is eradicating singleton from its apps. Why are you seeing fit to bring it back?
You can use a MBean and bind it to JNDI, then retrieve it wherever you want to use it.
The MBean might be deployed in a .sar file
If you are using Java EE 6, then it supports singleton EJBs.
If practical, simply take the class that has the singleton, put it in a JAR, take the JAR OUT of the the EAR, and add the JAR to the JBoss classloader (via the system classpath, or a some lib directory). This puts the class in a single classloader shared by both WARs.
The singleton will not be able to "see" anything in your applications WARs etc, as they're in a lower classloader.
However, there's nothing stopping you from injecting into the singleton (at server startup) a factory class, that originates from the WARs et al, and THAT class has access to all of the apps classes. That makes the singleton more of a simple container.
But this is straightforward to do.
Also, if you do this, make sure when you shut down you application, that any instances held by this singleton are freed. Any class reference by the singleton will not be GC'd when you undeploy the application. So, if you have a reference to your app stored in the singleton, then the server holds a reference to the singletons classloader, that classloader holds a reference to your singleton class, which holds reference to you apps class, which holds a reference to the apps CLASSLOADER, and THAT holds a reference to all of the classes in your app. Not a nice mess to leave behind.
Related
For my project I have to deploy several spring boot applications on a weblogic server. Each application will be a standalone war and will include a common library containing several singleton beans (you know, configurations, services, etcetera).
For example:
#Configuration
public class MyConf{
#Bean
public DataSource create(#Value("${myshareddataurl}")String dataurl){
return new MyDBDataSource();
}
}
I'm tempted on sharing the common lib because, being each war on the application server, the underlying JVM should be the same. So, suppose I create 10 singleton in my lib and I have 20 wars that would lead to 20x10=200 singletons taking the memspace while, by sharing, I only have 10.
On the other hand, I'm afraid this could lead to some unexpected runtime bug. Take my previous code snippet: what would happen if the "myshareddataurl" is defined in the spring boot's application properties? Would the singleton bean be re-instanciated with the overridden property? Or a new singleton bean is to be created?
And what would happen if every property I use in my common lib was referred in an external property file (so the same for every spring boot)?
I'd like to hear your opinions about this matter, I don't want to saturate my JVM memspace but I also need to know what is happening under the hood so to avoid bad configurations.
I want to shared common application context in a multi-war Spring application, so i don't need to defined beans again and again. But i don't want to instantiate those beans defined in this common application context in each webapp. I only want to instantiate those beans once and share by all the webapps. Is it possible? Bare me if i'm wrong.
PS: this common application context exists in jar, which i'll import to every webapps.
1, Below article tell us how to share the common application context, but need to instantiate those beans in each webapp.
How to import spring-config.xml of one project into spring-config.xml of another project?
2, Below is another article i just read, i demo it, but still don't get what i want, beans got instantiated twice. Can someone check this section "Why would you want to use this?" in the article, i don't get it, do they have a solution there, can someone help me here, thanks a lot for your time.
http://spring.io/blog/2007/06/11/using-a-shared-parent-application-context-in-a-multi-war-spring-application/
here is the demo source code in second article: https://github.com/jasonluo/ceciic/tree/master/Research/multiple-contexts-sample
Updates
The problem of case2 is because i didn't deploy as a ear file, thanks Deinum for pointing this out, i'm using tomcat, so there is no way to achieve that.
Our solution now is using REST to access the service webapp which running in a separate server.
Don't, there is usually classloader isolation going on to prevent this. Doing this right usually requires a thorough understanding of classloading, Java EE, packaging and your server.
Having that said there is way to do this as outlined in the blog you posted. You have to:
package both WARs into an EAR (this means you have to use and appserver like WildFly AS and can't just use a servlet engine like Tomcat or Jetty)
package both WARs as "skinny" WARs with at least Spring (and all its dependencies) and the shared beans (and all their dependencies) in the lib/ folder of the EAR instead of the WEB-INF/lib folder of the WARs.
Even then it depends on implementation details of the server. AFAIK it is not guaranteed to work under the Java EE specification. For example it likely won't work in GlassFish.
Update
I can't tell if the output of your demo is correct because I couldn't find a build file.
Yes, Tomcat does not support EARs, you could use Apache TomEE which is very similar and supports EARs (I don't know if TomEE does classloading in a way that makes this work). In theory you could also make use of the common classloader in Tomcat but that would be quite a hack.
There is a way to do it, using Spring Dynamic Modules, but it requires OSGi environment, which is quite different from simple Tomcat. Few articles worth reading:
Deploying Spring MVC applications to OSGi
Hello world style example
Blueprint - a further development on DM
Another blueprint documentation reference to Spring
That being said there is not a lot of up to date information about Spring with OSGi, but it's worth a try to achieve just what you said (but of course, with additional performance cost)
This may be a very rudimentary question, but please help me out if this is well-known and has been solved elsewhere.
I have a multi-war setup (all maven modules) say kilo-webapp1 and kilo-webapp2 as two WARs that I need to deploy on a Tomcat instance. These two webapps both use services from a common service jar, say kilo-common-services.jar. The kilo-common-services.jar has its own spring context that is loaded by the users of the jar viz. kilo-webapp1 and kilo-webapp2 in this case. It so happens that the initialization of the services in kilo-common-services takes a long time and hence I want it to happen only once (to ensure that the time it takes to bring up the instance is not very high) which also helps me to use it as a second level cache that it kept current in the JVM instance. To do this, we resorted to the following steps:
Modify the catalina.properties of CATALINA_BASE in tomcat to have shared.loader as ${catalina.base}/shared/lib
Copied the kilo-common-services.jar and all of its dependent jars to the CATALINA_BASE/shared/lib. [Manual step]
Copy spring related jars to the CATALINA_BASE/shared/lib location [Manual step]
Created a beanRefContext.xml file in kilo-common-services.jar. Define a new ClassPathXmlApplicationContext here, where the constructor was provided with the location to the spring context file for the common services.
Noted the dependency scope of kilo-common-services.jar and every other dependency (like Spring related jars) as provided in the kilo-webapp1 and kilo-webapp2 pom files. For Spring this is needed to ensure that the classpath scanning actions are not triggered twice. Also this causes different ClassCastExceptions (for log4j lets's say) if not excluded via the provided scope.
web.xml for kilo-webapp1 and kilo-webapp2 indicated that the parentContext for them is the servicesContext defined in kilo-common-services.jar.
I was able to verify that only one instance of the services of kilo-common-services exist, but the setup as you might have imagined is painful. If someone has best practices about such a setup in an IDE like Eclipse, would really appreciate it. My problems are as below:
#2 is becoming a challenge. I am currently running mvn dependency:copy-dependencies on kilo-common-services to copy dependent jars from target/dependency to the shared/lib which is a woefully manual step. Time and again, I forget to regenerate dependencies and have to do a redeploy again.
#3 is also not straight-forward as time and again there are newer common dependencies and we always have to remember to copy it to shared lib to avoid ClassCastExceptions
#5 is again a maintenance nightmare.
Also as time progresses, there will more such disparate common jars that need to be shared and it would involve pain for each of those jars. Feel free to critique the setup and propose a better one in its place that may be easy to use (from an IDE as well). Would be happy to provide any other details.
Thanks in advance!
The problem is that your architecture is broken (and that's why you're struggling with the solution). You have two solutions:
1) If you want to share a service that takes a long time (to initialise) between two war applications, make that a separate service completely and access it via rest or any kind of remoting.
2) Merge both webapps into one.
Having the common library is the shared lib folder is going to bring you lots of headaches, and you'll end up rolling it back.
My (personal) approach would be to merge both applications, but keep the packages separate enough and have separate spring configurations. In this way, at least you still keep the logic separation of both webapps.
Also since both run on the same container, there's little gain from having 2 separate wars (unless you're planning to move them to different containers very soon).
About the IDE, you can use the maven-cargo-plugin to start up a tomcat with several web applications with (almost) any configuration you want.
We are developing restful soa, with spring and tomcat and utilizing Domain Driven Design (well thats the plan anyway). There is migrationProject and a initial basic search service. Two separate WAR files, with two separate POMs. Both utilize the same Domain objects.
So I will have separate project that will be just the DomainObjects I will wrap them up into a jar, and then using maven and/or jenkins it will deploy automatically (whenever I configure (for example when pushed to a specific repository).
Having two copies of the same jar, sounds like a much worse idea to me. Its not your architecture that is broken, its your deployment and development process thats needs improvement, imho.
(my kind of related question).
Our long term plan is to have one project as the restful interface, with multiple Controllers that have service classes and repositories injected into them from their dependencies.
We have a portal application with one Main web app context and many minor web app contexts - plugins. Currently (very simplified) the Main one has own spring libraries and plugins would have to have them also if they wanted to use spring. In common/shared tomcat context there are just drivers and interfaces.
Would it work if spring libraries were moved to common context in regards to other libraries that spring might indirectly use or they might use spring ? Like hibernate, because the apps are using spring-tx etc. Would hibernate have to move to common/shared context too ?
What do you think, what are the other aspects ? From spring application context point of view it would be much easier like this.
#RichW is correct in stating that placing Spring libraries in Tomcat's common classloader is bad practice. And there's a good chance it won't work.
Java uses a classloader hierarchy). When a class load is requested, the classloader will recursively request the class from it's parent classloader before attempting to load the class using it's own classpath. This process continues up to the root classloader (know as the bootstrap classloader). In this way, classes referenced from a parent classloader always get priority over classes referenced in classloaders further down the hierarchy.
It's important to note that in this process classes are never loaded from a child classloader. Therefore any classes required by Spring would also need to be loaded into the common classloader - including asm, log4j, commons-logging and cglib (all of which spring depends on). This will lead to a whole host of problems: in particular, including commons-logging in the common classpath is a whole world of hurt
If you actually managed to get Tomcat started, then you would experience problems with memory leaks when recycling applications. In tomcat, applications are unloaded using conventional garbage collection, so if anything holds a reference to a class inside an application which has subsequently been restarted, that application will not get garbage collection. Spring and logging frameworks are prime candidates for holding references to classes so you will probably suffer from OOM errors after a few application restarts.
The only way to do this safely would be to consider using a full blown application server (such as JBoss AS) and deploy your application as an EAR.
If you were able to move from Tomcat to a full-blown Java EE container then an option would be to package everything as an EAR using the Bundled Optional Classes mechanism.
You'd then move the common JARs out of the WARs & into the top level of the EAR.
Yes, I know it's tempting. Yes, it can work. But putting application-specific or framework-specific libraries in the shared libraries folder of an app server is considered by some to be a bad practice, and I agree.
In my opinion web-apps should contain their own dependencies (app jars, framework jars, etc.). Frameworks also have dependencies, often requiring multiple jars with particular versions. Sometimes these versions change, sometimes the dependencies change. Over time that shared library folder will become a kitchen sink for jars, and that will affect all your apps, perhaps in unpredictable ways.
Going the shared library folder route you gain some slight initial convenience, but what you lose is choice: the choice to only affect one web-app at a time. I recommend you keep your jars within your web-app, nicely contained and separate from the other web-apps. It will make them more reliable and you'll find framework upgrades easier to handle. You'll be happier in the long run, I promise you.
I have a JavaEE6 application, consisting of Web stuff and EJBs and which is deployed as WAR-only (using EJB3.1). The build is based on Maven. I just read about a new possibility to order the module initialization in Java EE 6 here which i also need for my application. Also, i would like to have an option to define some EJB properties in XML.
Since the example is deployed as an EAR-project the order is defined in the application.xml. But in a WAR-deployed project, there is no application.xml. Now i wonder where i can define such informations? Or is it possible to use an application.xml somehow in a WAR-deployed-app?
EDIT:
Oops i didn't read the module-order-example right, in the first moment i thought it was about in which order the EJBs in my app are loaded. Of course i have only one module in my WAR-app, so ordering makes no sense.
Ok, but as i'm at it, one big question remains (also altered the question title to reflect the change): What about the ejb-jar.xml? Can i somehow define stuff about my EJBs in XML (as its useful for some settings, to avoid recompilation)?
In short, it is not possible with a WAR based deployment.
The module initialization feature of Java EE 6 is meant for initializing different modules of an application in a specific order. The moment you have a WAR based EJB application, you no longer have separate modules for your EJB and Web application. There is just one module - the web application module in a WAR based deployment.
Therefore, if you have to achieve the same feature as the module initialization order, offered in Java EE 6, you'll have to do either of the following:
Separate the EJB into a separate module, and use a EAR based deployment.
This is more or less trickery, as was done in Java EE 5, and you would want to be avoiding it. You might want to code in logic to ensure that the singleton EJBs have been created (assuming that this is due to the use of singletons in your application), before they're utilized in code.
Location of the ejb-jar.xml in a WAR file
The EJB 3.1 specification (in the chapter on Packaging) addresses the issue of the location of the ejb-jar.xml file when deployed in a WAR:
In a .war file, the deployment
descriptor is stored with the name
WEB-INF/ejb-jar.xml.
PS: I haven't tried this style of deployment yet. YMMV.
Side note on EJBs in .wars and ejb-jar.xml processing. As already noted the location is WEB-INF/ejb-jar.xml, but also note that is the only location checked even if there are ejbs jars inside WEB-INF/lib/ -- via standard rules any META-INF/ejb-jar.xml files there are ignored.
The Expert Group was rather split on that one, so if you have a preference it's not too late to send feedback to the EJB 3.1 expert group list for consideration in EJB.next.
My vote was to still allow individual jars to have META-INF/ejb-jar.xml files just as these jars can now have persistence.xmls, beans.xmls, web fragments etc. The larger issue for me was that it is at odds with the Embedded EJB Container API which supports an EAR-style classpath which allows several jars/modules each possibly containing a META-INF/ejb-jar.xml file. The result being if you do use the Embedded API to test a multi-jar ejb app that is composed into a single .war file, you are then faced with the task of merging any ejb-jar.xml data you have into a single ejb-jar.xml for the webapp. Sort of a pain for users.