Version 4

    Using Tomcat DataSources when deploying Seam with JBoss Microcontainer

     

    Currently, when deploying Seam applications on Tomcat using JBoss Microcontainer, the DataSource definition for the application is to be placed in jboss-beans.xml file (located INSIDE the deployed WAR). A common (and de-facto standard) requirement for production deployments is to externalize the DataSource definition outside the application WAR (so as not to expose production DB credentials to developers), either in Tomcat's server.xml file, or in META-INF/context.xml. The code below fills the gap, allowing Seam applications to be deployed on Tomcat, while allowing to use external DataSources defined in Tomcat.  Note that for the import statements to resolve, seam and either jboss or the jboss microcontainer needs to be on the classpath.

     

    
    import java.util.Properties;
    import javax.naming.InitialContext;
    import javax.naming.Name;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    import org.jboss.deployment.DeploymentException;
    import org.jboss.seam.util.Naming;
    import org.jboss.util.naming.NonSerializableFactory;
    import org.jboss.util.naming.Util;
    
    
    public class TomcatDataSourceFactory {
         private String dataSourceName;
         private DataSource ds;
    
         protected void bindConnectionFactory() throws Exception {
              InitialContext initialContext = new InitialContext(
                        Naming.getInitialContextProperties());
              InitialContext ctx = initialContext;
              try {
                   Name name = ctx.getNameParser("").parse(dataSourceName);
                   String key = name.toString();
                   if (true == true && name.size() > 1) {
                        int size = name.size() - 1;
                        Util.createSubcontext(initialContext, name.getPrefix(size));
                   }
                   NonSerializableFactory.rebind(initialContext, key, ds);
              } catch (NamingException ne) {
                   throw new DeploymentException(
                             "Could not bind ConnectionFactory into jndi: "
                                       + dataSourceName, ne);
              } finally {
                   ctx.close();
              }
         }
         public Object getDataSource() throws Exception {
              if (ds == null) {
                   Properties props = new Properties();
                   props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
                             "org.apache.naming.java.javaURLContextFactory");
                   InitialContext ctx = new InitialContext(props);
                   ds = (DataSource) ctx.lookup(dataSourceName);
                   bindConnectionFactory();
              }
              return ds;
         }
         public void setDataSourceName(String dataSourceName) {
              this.dataSourceName = dataSourceName;
         }
    }
    

     

    In jboss-beans.xml, the following would replace the definition of the DatasourceFactory:

     

         <bean name="DatasourceFactory"
              class="youir.package.name.TomcatDataSourceFactory">
              <property name="dataSourceName">java:comp/env/DataSource</property>
         </bean>
    

     

    where dataSourceName would point to the DataSource defined in Tomcat.

     

    NB: the bindConnectionFactory() method is almost an exact copy of org.jboss.resource.adapter.jdbc.local.LocalTxDataSource.bindConnectionFactory() method.  LocalTxDataSource.java can be found here: 

    http://fisheye.jboss.com/browse/~raw,r=1.11/JBoss/jbosscx/src/main/org/jboss/resource/adapter/jdbc/local/LocalTxDataSource.java