tomee-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Luis Fernando Planella Gonzalez <lfpg....@gmail.com>
Subject Database configuration on web application startup
Date Fri, 22 May 2009 18:13:53 GMT
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

Mime
  • Unnamed multipart/alternative (inline, 7-Bit, 0 bytes)
View raw message