Hi. First of all, sorry for the long post.
Some time has past since my last thread about programmatic database deployment 
on the web application startup.
First of all, the application is Cyclos (http://project.cyclos.org), which have 
the following requirements:
* The application is being rewritten in GWT / EJB3 (previously was Struts / 
Hibernate).
* It's an open source web application which may be downloaded and installed. 
* Users are used to having a single .properties to customize the database 
access. If possible, I'd like to keep the connection in a .properties inside 
the application.
* Several instances of the same application may be deployed in the same server. 
So, even that the user could create a data source in tomcat/conf/openejb.xml, 
he would have to change each application instance's META-INF/persistence.xml, 
which is packed inside a jar, inside another war. So, this is the showstopper. 
If weren't for this, it wouldn't be that bad to make the user set the data 
source in the tomcat file.

My goal: having each deployed application instance to have an automatically 
assigned data source, so that the user don't have to unpack the war, jar, 
modify the META-INF/persistence.xml and then repack.
Ideally, if it were possible use a variable in persistence.xml (something like 
<persistence-unit><jta-data-source>${webApplicationContext}</jta-data-source>...),
 it would be perfect. Perhaps, it wouldn't be hard to implement this in OpenEJB 
(probably in TomcatWebAppBuilder).

So, to make things work, I started to research, and here is the solution I 
found (and really not satisfied with it, since it's an ugly hack, and not 
comfortable to put it in production):
* I created a custom class for the Tomcat Context handle, setting 
META-INF/context.xml with <Context 
className="nl.strohalm.cyclos.tomcat.CyclosContext">
* I had to create a custom org.apache.openejb.config.DynamicDeployer which 
would wrap the existing deployer and do something else. However, since 
TomcatWebAppBuilder uses a ConfigurationFactory (but don't have a public 
getter), and ConfigurationFactory doesn't allow a custom deployer, I had to set 
a few private attributes through reflection:
        WebAppBuilder webAppBuilder = 
systemInstance.getComponent(WebAppBuilder.class);
        try {
            Field configurationFactoryField = 
webAppBuilder.getClass().getDeclaredField("configurationFactory");
            configurationFactoryField.setAccessible(true);
            ConfigurationFactory configurationFactory = (ConfigurationFactory) 
configurationFactoryField.get(webAppBuilder);
            Field deployerField = 
configurationFactory.getClass().getDeclaredField("deployer");
            deployerField.setAccessible(true);
            DynamicDeployer currentDeployer = (DynamicDeployer) 
deployerField.get(configurationFactory);
            // HERE: Setting the deployer through reflection, as there's no api 
for setting it.
            CyclosDeployer deployer = new CyclosDeployer(currentDeployer);
            deployerField.set(configurationFactory, deployer);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }

* Ok, I had my custom deployer in, but in order to set a custom value for the 
data source, I had a problem: The standard deployer is a 
org.apache.openejb.config.ConfigurationFactory.Chain, with several other 
deployers. The first one is ReadDescriptors and the others would actually 
deploy the beans. However, to change the data source, my deployer would have to 
run after ReadDescriptors (otherwise there would be no knowledge about any 
persistence units), but before the other deployers, or the wrong data source 
would be already deployed when my deployer would be invoked. So, another hack: 
by reflection again, retrieve the Chain's deployers and remove the 
ReadDescriptors from there. Then, my deploy method:
    public AppModule deploy(AppModule appModule) throws OpenEJBException {
        //The ReadDescriptors must run in order to have persistence modules
        new ReadDescriptors().deploy(appModule);

        for (PersistenceModule pm : appModule.getPersistenceModules()) {
            for (PersistenceUnit unit : 
pm.getPersistence().getPersistenceUnit()) {
                if ("cyclos".equals(unit.getName())) {
                    String path = "/" + 
appModule.getWebModules().get(0).getContextRoot();
                    String dataSource = dataSources.get(path);
                    if (dataSource == null) {
                        throw new IllegalStateException("Could not find the 
datasource for Cyclos instance at " + path);
                    }
                    //Set the data source name
                    unit.setJtaDataSource(dataSource);
                    unit.setNonJtaDataSource(dataSource + "_nonJta");
                }
            }
        }

        return delegate.deploy(appModule);
    }

* And, since I was this deep, I've used the ConfigurationFactory / Assembler to 
deploy the DataSource as David suggested, but I could live with a predefined 
data source.

So, here's my desperate cry: Is it possible to implement the data-source name 
being calculated per web-application? 
If not, does someone has a better idea?

Thanks and again, sorry for the long post

Luis Fernando Planella Gonzalez

Reply via email to