You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jl...@apache.org on 2010/12/16 12:12:49 UTC

svn commit: r1049930 - in /openejb/trunk/openejb3/src: main/java/ main/java/org/ main/java/org/apache/ main/java/org/apache/openejb/ main/java/org/apache/openejb/resource/ main/java/org/apache/openejb/resource/jdbc/ test/ test/java/ test/java/org/ test...

Author: jlmonteiro
Date: Thu Dec 16 11:12:48 2010
New Revision: 1049930

URL: http://svn.apache.org/viewvc?rev=1049930&view=rev
Log:
OPENEJB-1410 Dynamic DS routing. Patch from Romain Manni-Bucau. Thx!

Added:
    openejb/trunk/openejb3/src/main/java/
    openejb/trunk/openejb3/src/main/java/org/
    openejb/trunk/openejb3/src/main/java/org/apache/
    openejb/trunk/openejb3/src/main/java/org/apache/openejb/
    openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/
    openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/
    openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java
    openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/Router.java
    openejb/trunk/openejb3/src/test/
    openejb/trunk/openejb3/src/test/java/
    openejb/trunk/openejb3/src/test/java/org/
    openejb/trunk/openejb3/src/test/java/org/apache/
    openejb/trunk/openejb3/src/test/java/org/apache/openejb/
    openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/
    openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/
    openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/DynamicDataSourceTest.java
    openejb/trunk/openejb3/src/test/resources/
    openejb/trunk/openejb3/src/test/resources/META-INF/
    openejb/trunk/openejb3/src/test/resources/META-INF/org.router/
    openejb/trunk/openejb3/src/test/resources/META-INF/org.router/service-jar.xml

Added: openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java?rev=1049930&view=auto
==============================================================================
--- openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java (added)
+++ openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java Thu Dec 16 11:12:48 2010
@@ -0,0 +1,131 @@
+package org.apache.openejb.resource.jdbc;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.ContainerSystem;
+
+public class RoutedDataSource implements DataSource {
+    private static final String OPENEJB_RESOURCE_PREFIX = "openejb:Resource/";
+
+    private Router delegate;
+
+    public RoutedDataSource() {
+        // no-op
+    }
+
+    public RoutedDataSource(String router) {
+        setRouter(router);
+    }
+
+    public void setRouter(String router) {
+        ContainerSystem containerSystem = SystemInstance.get().getComponent(
+                ContainerSystem.class);
+
+        Object o = null;
+        Context ctx = containerSystem.getJNDIContext();
+        try {
+            o = ctx.lookup(OPENEJB_RESOURCE_PREFIX + router);
+        } catch (NamingException e) {
+            throw new IllegalArgumentException("Can't find router [" + router
+                    + "]", e);
+        }
+
+        if (o instanceof Router) {
+            delegate = (Router) o;
+        } else {
+            throw new IllegalArgumentException(o + " is not a router");
+        }
+    }
+
+    public PrintWriter getLogWriter() throws SQLException {
+        if (ds() == null) {
+            return null;
+        }
+        return ds().getLogWriter();
+    }
+
+    public void setLogWriter(PrintWriter out) throws SQLException {
+        if (ds() != null) {
+            ds().setLogWriter(out);
+        }
+    }
+
+    public void setLoginTimeout(int seconds) throws SQLException {
+        if (ds() != null) {
+            ds().setLoginTimeout(seconds);
+        }
+    }
+
+    public int getLoginTimeout() throws SQLException {
+        if (ds() == null) {
+            return -1;
+        }
+        return ds().getLoginTimeout();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T unwrap(Class<T> iface) throws SQLException {
+        if (ds() == null) {
+            return null;
+        }
+        return (T) callByReflection(ds(), "unwrap",
+                new Class<?>[] { Class.class },  new Object[] { iface });
+    }
+
+    public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        if (ds() == null) {
+            return false;
+        }
+        return (Boolean) callByReflection(ds(), "isWrapperFor",
+                new Class<?>[] { Class.class },  new Object[] { iface });
+    }
+
+    public Connection getConnection() throws SQLException {
+        return ds().getConnection();
+    }
+
+    public Connection getConnection(String username, String password)
+            throws SQLException {
+        return ds().getConnection(username, password);
+    }
+
+    public Router getDelegate() {
+        if (delegate == null) {
+            throw new IllegalStateException("a router has to be defined");
+        }
+        return delegate;
+    }
+
+    private DataSource ds() {
+        return getDelegate().getDataSource();
+    }
+
+    /**
+     * This method is used to avoir to fork two projects to implement
+     * datasource of the jre5 and jre6
+     *
+     * @param ds the wrapped datasource
+     * @param mtdName the method to call
+     * @param paramTypes the parameter type
+     * @param args the arguments
+     * @return the return value of the method
+     */
+    private Object callByReflection(DataSource ds, String mtdName,
+            Class<?>[] paramTypes, Object[] args) {
+        Method mtd;
+        try {
+            mtd = ds.getClass().getDeclaredMethod(mtdName, paramTypes);
+            return mtd.invoke(ds, args);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}

Added: openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/Router.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/Router.java?rev=1049930&view=auto
==============================================================================
--- openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/Router.java (added)
+++ openejb/trunk/openejb3/src/main/java/org/apache/openejb/resource/jdbc/Router.java Thu Dec 16 11:12:48 2010
@@ -0,0 +1,7 @@
+package org.apache.openejb.resource.jdbc;
+
+import javax.sql.DataSource;
+
+public interface Router {
+    DataSource getDataSource();
+}

Added: openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/DynamicDataSourceTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/DynamicDataSourceTest.java?rev=1049930&view=auto
==============================================================================
--- openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/DynamicDataSourceTest.java (added)
+++ openejb/trunk/openejb3/src/test/java/org/apache/openejb/router/test/DynamicDataSourceTest.java Thu Dec 16 11:12:48 2010
@@ -0,0 +1,284 @@
+package org.apache.openejb.router.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.ejb.Local;
+import javax.ejb.Stateless;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.Id;
+import javax.persistence.PersistenceContext;
+import javax.sql.DataSource;
+
+import org.apache.openejb.assembler.classic.Assembler;
+import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
+import org.apache.openejb.assembler.classic.ResourceInfo;
+import org.apache.openejb.assembler.classic.SecurityServiceInfo;
+import org.apache.openejb.assembler.classic.StatelessSessionContainerInfo;
+import org.apache.openejb.assembler.classic.TransactionServiceInfo;
+import org.apache.openejb.client.LocalInitialContextFactory;
+import org.apache.openejb.config.AppModule;
+import org.apache.openejb.config.ConfigurationFactory;
+import org.apache.openejb.config.EjbModule;
+import org.apache.openejb.config.PersistenceModule;
+import org.apache.openejb.config.sys.Resource;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.StatelessBean;
+import org.apache.openejb.jee.jpa.unit.Persistence;
+import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
+import org.apache.openejb.jee.jpa.unit.TransactionType;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.resource.jdbc.Router;
+import org.apache.openejb.spi.ContainerSystem;
+import org.hsqldb.jdbcDriver;
+import org.junit.Test;
+
+public class DynamicDataSourceTest {
+    @Test
+    public void route() throws Exception {
+        System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName());
+
+        ConfigurationFactory config = new ConfigurationFactory();
+
+        Assembler assembler = new Assembler();
+        assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class));
+        assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
+        assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
+
+        // resources
+        for (int i = 1; i <= 3; i++) {
+            String dbName = "database" + i;
+            Resource resourceDs = new Resource(dbName, "DataSource");
+            Properties p = resourceDs.getProperties();
+            p.put("JdbcDriver", "org.hsqldb.jdbcDriver");
+            p.put("JdbcUrl", "jdbc:hsqldb:mem:db" + i);
+            p.put("UserName", "sa");
+            p.put("Password", "");
+            p.put("JtaManaged", "true");
+            assembler.createResource(config.configureService(resourceDs, ResourceInfo.class));
+        }
+        Resource resourceRouter = new Resource("My Router", "org.apache.openejb.router.test.DynamicDataSourceTest$DeterminedRouter", "org.router:DeterminedRouter");
+        resourceRouter.getProperties().setProperty("DatasourceNames", "database1 database2 database3");
+        resourceRouter.getProperties().setProperty("DefaultDataSourceName", "database1");
+        assembler.createResource(config.configureService(resourceRouter, ResourceInfo.class));
+
+        Resource resourceRoutedDs = new Resource("Routed Datasource", "org.apache.openejb.resource.jdbc.Router", "org.apache.openejb.dynamicdatasource:RoutedDataSource");
+        resourceRoutedDs.getProperties().setProperty("Router", "My Router");
+        assembler.createResource(config.configureService(resourceRoutedDs, ResourceInfo.class));
+
+        // containers
+        StatelessSessionContainerInfo statelessContainerInfo = config.configureService(StatelessSessionContainerInfo.class);
+        assembler.createContainer(statelessContainerInfo);
+
+        EjbJar ejbJar = new EjbJar();
+        ejbJar.addEnterpriseBean(new StatelessBean(RoutedEJBBean.class));
+
+        EjbModule ejbModule = new EjbModule(ejbJar);
+
+        // Create an "ear"
+        AppModule appModule = new AppModule(ejbModule.getClassLoader(), ejbModule.getJarLocation());
+        appModule.getEjbModules().add(ejbModule);
+
+        // Create a persistence-units
+        PersistenceUnit unit = new PersistenceUnit("router");
+        unit.addClass(Person.class);
+        unit.setTransactionType(TransactionType.JTA);
+        unit.setJtaDataSource("Routed Datasource");
+        appModule.getPersistenceModules().add(new PersistenceModule("root", new Persistence(unit)));
+
+        assembler.createApplication(config.configureApplication(appModule));
+
+        // context
+        Context ctx = new InitialContext();
+
+        // creating database
+        // openjpa creates it when the entity manager (em) is invoked the first
+        // time but we need to create tables before the first use of the em
+        // so it is done using jdbc
+        Class.forName(jdbcDriver.class.getName());
+        for (int i = 1; i <= 3; i++) {
+            Connection connection = DriverManager.getConnection(
+                    "jdbc:hsqldb:mem:db" + i, "sa", "");
+            Statement st = connection.createStatement();
+            st.executeUpdate("CREATE TABLE \"DynamicDataSourceTest$Person\" (id BIGINT NOT NULL, name VARCHAR(255), PRIMARY KEY (id))");
+            st.close();
+            connection.close();
+        }
+
+        // running persist on all "routed" databases
+        final List<String> databases = new ArrayList<String>();
+        databases.add("database1");
+        databases.add("database2");
+        databases.add("database3");
+
+        RoutedEJB ejb = (RoutedEJB) ctx.lookup("RoutedEJBBeanLocal");
+        for (int i = 0; i < 18; i++) {
+            String name = "record " + i;
+            String db = databases.get(i % 3);
+            ejb.persist(i, name, db);
+        }
+
+        // assert database record number using jdbc
+        for (int i = 1; i <= 3; i++) {
+            Connection connection = DriverManager.getConnection(
+                    "jdbc:hsqldb:mem:db" + i, "sa", "");
+            Statement st = connection.createStatement();
+            ResultSet rs = st.executeQuery("select count(*) from \"DynamicDataSourceTest$Person\"");
+            rs.next();
+            assertEquals(6, rs.getInt(1));
+            st.close();
+            connection.close();
+        }
+    }
+
+    @Stateless
+    @Local(RoutedEJB.class)
+    public static class RoutedEJBBean implements RoutedEJB {
+        @PersistenceContext(unitName = "router")
+        private EntityManager em;
+
+        @javax.annotation.Resource(name = "My Router", type = DeterminedRouter.class)
+        private DeterminedRouter router;
+
+        public void persist(int id, String name, String ds) {
+            router.setDatasource(ds);
+            em.persist(new Person(id, name));
+        }
+    }
+
+    public static interface RoutedEJB {
+        void persist(int id, String name, String ds);
+    }
+
+    @Entity
+    public static class Person {
+        @Id
+        private long id;
+        private String name;
+
+        public Person() {
+            // no-op
+        }
+
+        public Person(int i, String n) {
+            id = i;
+            name = n;
+        }
+
+        public long getId() {
+            return id;
+        }
+
+        public void setId(long id) {
+            this.id = id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    public static class DeterminedRouter implements Router {
+        private String dataSourceNames;
+        private String defaultDataSourceName;
+        private Map<String, DataSource> dataSources = null;
+        private ThreadLocal<DataSource> currentDataSource = new ThreadLocal<DataSource>();
+
+        /**
+         * @param datasourceList datasource resource name, separator is a space
+         */
+        public void setDataSourceNames(String datasourceList) {
+            dataSourceNames = datasourceList;
+        }
+
+        /**
+         * lookup datasource in openejb resources
+         */
+        private void init() {
+            dataSources = new ConcurrentHashMap<String, DataSource>();
+            for (String ds : dataSourceNames.split(" ")) {
+                ContainerSystem containerSystem = SystemInstance.get()
+                        .getComponent(ContainerSystem.class);
+
+                Object o = null;
+                Context ctx = containerSystem.getJNDIContext();
+                try {
+                    o = ctx.lookup("openejb:Resource/" + ds);
+                    if (o instanceof DataSource) {
+                        dataSources.put(ds, (DataSource) o);
+                    } else {
+                    }
+                } catch (NamingException e) {
+                }
+            }
+        }
+
+        /**
+         * @return the user selected data source if it is set
+         *         or the default one
+         *  @throws IllegalArgumentException if the data source is not found
+         */
+        public DataSource getDataSource() {
+            // lazy init of routed datasources
+            if (dataSources == null) {
+                init();
+            }
+
+            // if no datasource is selected use the default one
+            if (currentDataSource.get() == null) {
+                if (dataSources.containsKey(defaultDataSourceName)) {
+                    return dataSources.get(defaultDataSourceName);
+                } else {
+                    throw new IllegalArgumentException("you have to specify at least one datasource");
+                }
+            }
+
+            // the developper set the datasource to use
+            return currentDataSource.get();
+        }
+
+        /**
+         *
+         * @param ds data source name
+         */
+        public void setDatasource(String datasourceName) {
+            if (dataSources == null) {
+                init();
+            }
+            if (!dataSources.containsKey(datasourceName)) {
+                throw new IllegalArgumentException("data source called "
+                        + datasourceName + " can't be found.");
+            }
+            DataSource ds = dataSources.get(datasourceName);
+            currentDataSource.set(ds);
+        }
+
+        /**
+         * reset the data source
+         */
+        public void clear() {
+            currentDataSource.remove();
+        }
+
+        public void setDefaultDataSourceName(String name) {
+            this.defaultDataSourceName = name;
+        }
+    }
+}

Added: openejb/trunk/openejb3/src/test/resources/META-INF/org.router/service-jar.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/src/test/resources/META-INF/org.router/service-jar.xml?rev=1049930&view=auto
==============================================================================
--- openejb/trunk/openejb3/src/test/resources/META-INF/org.router/service-jar.xml (added)
+++ openejb/trunk/openejb3/src/test/resources/META-INF/org.router/service-jar.xml Thu Dec 16 11:12:48 2010
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ServiceJar>
+  <ServiceProvider id="DeterminedRouter" service="Resource"
+    type="org.apache.openejb.resource.jdbc.Router"
+    class-name="org.apache.openejb.router.test.DynamicDataSourceTest$DeterminedRouter">
+    DataSourceNames
+    DefaultDataSourceName
+  </ServiceProvider>
+</ServiceJar>