views:

274

answers:

4

I have a Java web application leveraging JPA. The database instance is specified in the persistence.xml file using the jta-data-source tag.

I would like to deploy a second copy of the web application on the same (glassfish) server, but pointed at a different database instance.

What is the best way to achieve this? I think ideally, I'd like to place a persistence.xml override file outside of the war (somewhere on the classpath?). I'm not exactly sure where to put it or how to define it in a way that it wouldn't confilt with my other instance. I can see us hosting a handful of instances in this manner (SaaS) and I'd like the configuration to be outside the deployed war so that it wouldn't become a maintenance issue. Sharing a database instance between clients is not an option for security reasons.

I'm sure this isn't a unique problem to our group. What are the best practices or solutions others are using to solve this problem?

+1  A: 

I would package two versions of the webapp and declare a specific datasource in the jta-data-source element of their respective persistence.xml (I don't think you can externalize this file, it is expected to be in WEB-INF/classes/META-INF/persistence.xml)

Pascal Thivent
Ah, I feared that. Building multiple times to make a simple config change seems like a pain from a maintenance perspective as the application matures, more clients are added, and new versions are released (10 clients means 10 separate build and deploy cycles with changes to the persistence file in between). I use maven to build and package. Do you know if there is a maven plugin that would be able to generate multiple wars, taking the appropriate persistence xml for each configuration?
Vinnie
@Vinnie: I'd use [filtering and profiles](http://www.sonatype.com/books/mvnref-book/reference/resource-filtering-sect-description.html) (and let my CI engine build all variants using a Build Matrix). Declare something like `<jta-data-source>${foo}</jta-data-source>` in your `persistence.xml` and use Maven to filter the value.
Pascal Thivent
+1  A: 

i guess you are already altering the copy in other things too ? (e.g. name of ear file)

If you use a build software like ant or maven you could use a placeholder inside the persistence.xml and create a different "copy" by just using different build parameters.

e.g. with maven

mvn clean install -DmyDatabaseName=db/somedb

Michael Lange
OK, that sounds cool. What do I need to place into my persistence.xml as the placeholder for the text substitution to take place?
Vinnie
see the comment from Pascal below :-)
Michael Lange
A: 

ahhh vinnie i feel your pain!

i've been looking for a solution to this for so long as well. and the solutions offered are always along the same lines.

i can't believ an 'ENTERPRISE' component platform as JEE is supposed to be is so crumby. i guess they've only had six chances over what? ten years to get this right.

the fact that runtime configuration is embedded INSIDE the actual deployables and there is no way to override them is the biggest JEE spec joke.

even worse, when using EAR files, the database is hard coded inside a deployable of the deployable. so in the case above for an ear you'd have to unzip the ear file, possibly unzip every jar file, change one line in a descriptor, rezip everything and then redeploy for every seperate deployment. i have spent countless hours reading the specs trying to find the fix for this - as i simple could not BELIEVE there would be none for such an obvious situation.

and this is how its supposed to be? this is the best we can do? even for third party components supplied by someone else? still? in 2010? really? you're serious?

hows this for a consumer product brochure - in order to change the channel on your tv, get a phillips head screwdriver and take the back off your remote control, pull apart the board and xxxxx....

i know you can change the global database def from the appserver, but that's a hack (why not just change your DNS settings in the hosts file to point to a different database machine?) and doesn't help for multiple installs of the same component.

hows this for a consumer product brochure - 'your tv remote contains one channel select key - '7'. in order to change to an alternate channel, reconfigure the channel memory allocations so that your desired viewing channel aligns with memory allocation '7'. then press the '7' key on your remote.

are we to understand that a whole appserver should really only ever be used for one deployment? you know - all that complex classloader stuff means nothing if you (OH MY ^&*#$% GOD) try to use the SAME unaltered jar in another unrelated deployment! thats just a concept too far-out for the JEE expert group - who'd ever think to do that?

we were trying to use ejb jar files as deployable components accross a company. this fact made it a total nightmare. you cant just store the jar in maven like everything else and share it around without restrictions - guess what the problem was? YES! people wanted to use these great jars in seperate apps pointing at different databases, in the same app server. the maven ear plugin allows you to replace the WHOLE descriptor of an ejb-jar file during deployment - but guess what happened when we released a bugfix of the component into maven? YES! you've figured it out haven't you? they OVERWROTE the new descriptor with their old one! just because they wanted to tweak the database pointer!

the only point i can come up with in regard to all this is that: NOBODY IS ACTUALLY USING JEE FOR COMPONENT DEVELOPMENT. the solution offered seem to indicate its assumed YOU control ALL the code and ALL the code is inside your unit of work. in which case you can use maven profiles and so forth to do substitutions.

now i'm sure we could write endless plugins in maven or ant to come up with some great system that opens up zips and makes xpath based changes to existing files, but really? doesn't the need point out the fact theres something fundamentally ^&*(ed up with the system as it is?

i'm currently trying to see if theres some way to programatically create the entitymanager factory so you can set the database at runtime from some external config, then use that to inject to the dao's. a nice little side project in order to try and finish the actual project. goody.

to be fair particularly with 6 JEE's generally pretty good, but no matter how close to ok it gets there is always something left out which makes my life a misery.

case in point.

john sanders
A: 

update:

http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/configuration.html

not sure if this can be set up in an appserver but i'll give it a go. only thing that worries me is management of the scope. the container manages the scope along transaction boundaries

//inject entity manager
@Inject("mySpecialEntityManager")
EntityManager em;

//then mark the actual factory method in the factory bean with    
@Produces("mySpecialEntityManager")

but then how to manage scope is the problem


2.2.2. Bootstrapping

The JPA specification defines a bootstrap procedure to access the EntityManagerFactory and the EntityManager. The bootstrap class is javax.persistence.Persistence, e.g.

Map configOverrides = new HashMap();

configOverrides.put("hibernate.hbm2ddl.auto", "create-drop");

EntityManagerFactory programmaticEmf =

Persistence.createEntityManagerFactory("manager1", configOverrides);

The first version is equivalent to the second with an empty map. The map version is a set of overrides that will take precedence over any properties defined in your persistence.xml files. All the properties defined in Section 2.2.1, “Packaging” can be passed to the createEntityManagerFactory method and there are a few additional ones:

*

  javax.persistence.provider to define the provider class used
*

  javax.persistence.transactionType to define the transaction type used (either JTA or RESOURCE_LOCAL)
*

  javax.persistence.jtaDataSource to define the JTA datasource name in JNDI
*

  javax.persistence.nonJtaDataSource to define the non JTA datasource name in JNDI
*

  javax.persistence.lock.timeout pessimistic lock timeout in milliseconds (Integer or String)
*

  javax.persistence.query.timeout query timeout in milliseconds (Integer or String)
*

  javax.persistence.sharedCache.mode corresponds to the share-cache-mode element defined in Section 2.2.1, “Packaging”.
john sanders