Author: rmannibucau Date: Thu Apr 25 21:54:08 2013 New Revision: 1475982 URL: http://svn.apache.org/r1475982 Log: TOMEE-912 adding FailoverRouter
Added: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/ConnectionProvider.java tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/AbstractRouter.java Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java?rev=1475982&r1=1475981&r2=1475982&view=diff ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java (original) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java Thu Apr 25 21:54:08 2013 @@ -1552,12 +1552,26 @@ public class ConfigurationFactory implem public String getReference(final ResourceInfo info) { for (Object value : info.properties.values()) { if (String.class.isInstance(value)) { - value = ((String) value).trim(); - if (ids.contains(value)) { + final String string = String.class.cast(value).trim(); + if (string.isEmpty()) { + continue; + } + + if (ids.contains(string)) { return (String) value; } + + if (string.contains(",")) { // multiple references + for (final String s : string.split(",")) { + final String trimmed = s.trim(); + if (ids.contains(trimmed)) { + return s; + } + } + } + for (final String s : ids) { - if (s.endsWith((String) value)) { + if (s.endsWith("/" + string)) { // submodule resources return s; } } Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java?rev=1475982&r1=1475981&r2=1475982&view=diff ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java (original) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/RoutedDataSource.java Thu Apr 25 21:54:08 2013 @@ -16,22 +16,20 @@ */ package org.apache.openejb.resource.jdbc; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.resource.jdbc.router.ConnectionProvider; +import org.apache.openejb.resource.jdbc.router.Router; +import org.apache.openejb.spi.ContainerSystem; +import org.apache.openejb.util.reflection.Reflections; + +import javax.naming.NamingException; +import javax.sql.DataSource; import java.io.PrintWriter; -import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.sql.DataSource; - -import org.apache.openejb.loader.SystemInstance; -import org.apache.openejb.resource.jdbc.router.Router; -import org.apache.openejb.spi.ContainerSystem; -import org.apache.openejb.util.reflection.Reflections; - public class RoutedDataSource implements DataSource { private static final String OPENEJB_RESOURCE_PREFIX = "openejb:Resource/"; @@ -46,20 +44,15 @@ public class RoutedDataSource implements } public void setRouter(String router) { - ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class); - - Object o = null; - Context ctx = containerSystem.getJNDIContext(); + Object o; try { - o = ctx.lookup(OPENEJB_RESOURCE_PREFIX + router); - + o = SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext().lookup(OPENEJB_RESOURCE_PREFIX + router); } catch (NamingException e) { throw new IllegalArgumentException("Can't find router [" + router + "]", e); } - if (o instanceof Router) { - delegate = (Router) o; - + if (Router.class.isInstance(o)) { + delegate = Router.class.cast(o); } else { throw new IllegalArgumentException(o + " is not a router"); } @@ -115,11 +108,17 @@ public class RoutedDataSource implements } public Connection getConnection() throws SQLException { + if (ConnectionProvider.class.isInstance(getDelegate())) { + return ConnectionProvider.class.cast(getDelegate()).getConnection(); + } return getTargetDataSource().getConnection(); } public Connection getConnection(String username, String password) throws SQLException { + if (ConnectionProvider.class.isInstance(getDelegate())) { + return ConnectionProvider.class.cast(getDelegate()).getConnection(username, password); + } return getTargetDataSource().getConnection(username, password); } Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/AbstractRouter.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/AbstractRouter.java?rev=1475982&r1=1475981&r2=1475982&view=diff ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/AbstractRouter.java (original) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/AbstractRouter.java Thu Apr 25 21:54:08 2013 @@ -24,18 +24,18 @@ import javax.naming.NamingException; public abstract class AbstractRouter implements Router { private static final String OPENEJB_RESOURCE = "openejb:Resource/"; - private Context ctx; + + private final Context ctx; public AbstractRouter() { - ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class); - ctx = containerSystem.getJNDIContext(); + ctx = SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext(); } - public Object getJndiResource(String name) throws NamingException { + protected Object getJndiResource(String name) throws NamingException { return ctx.lookup(name); } - public Object getOpenEJBResource(String name) throws NamingException { + protected Object getOpenEJBResource(String name) throws NamingException { return getJndiResource(OPENEJB_RESOURCE + name); } } Added: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/ConnectionProvider.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/ConnectionProvider.java?rev=1475982&view=auto ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/ConnectionProvider.java (added) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/ConnectionProvider.java Thu Apr 25 21:54:08 2013 @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.resource.jdbc.router; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface ConnectionProvider { + Connection getConnection() throws SQLException; + Connection getConnection(String user, String pwd) throws SQLException; +} Added: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java?rev=1475982&view=auto ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java (added) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java Thu Apr 25 21:54:08 2013 @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.resource.jdbc.router; + +import org.apache.openejb.util.LogCategory; +import org.apache.openejb.util.Logger; + +import javax.naming.NamingException; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Pattern; + +public class FailOverRouter extends AbstractRouter implements ConnectionProvider { + private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_SERVER, FailOverRouter.class); + + private String delimiter = ","; + private String datasourceNames = ""; + + private Collection<DataSource> dataSources = new CopyOnWriteArrayList<DataSource>(); + + @Override + public DataSource getDataSource() { + for (final DataSource ds : dataSources) { + return ds; + } + throw new IllegalStateException("No datasource configured"); + } + + @Override + public Connection getConnection() throws SQLException { + for (final DataSource ds : dataSources) { + try { + return ds.getConnection(); + } catch (final SQLException e) { + // no-op + } + } + throw new SQLException("Can't connect to any datasources of " + dataSources); + } + + @Override + public Connection getConnection(String user, String pwd) throws SQLException { + for (final DataSource ds : dataSources) { + try { + return ds.getConnection(user, pwd); + } catch (final SQLException e) { + // no-op + } + } + throw new SQLException("Can't connect to any datasources of " + dataSources); + } + + public void setDatasourceNames(final String datasourceNames) { + this.datasourceNames = datasourceNames; + initDataSources(); + } + + public void setDelimiter(final String delimiter) { + this.delimiter = delimiter; + initDataSources(); + } + + private void initDataSources() { + for (final String ds : datasourceNames.split(Pattern.quote(delimiter))) { + try { + final Object o = getOpenEJBResource(ds.trim()); + if (DataSource.class.isInstance(o)) { + LOGGER.debug("Found datasource '" + ds + "'"); + dataSources.add(DataSource.class.cast(o)); + } + } catch (final NamingException error) { + LOGGER.error("Can't find datasource '" + ds + "'", error); + } + } + } + + public Collection<DataSource> getDataSources() { + return dataSources; + } + + public void updateDataSources(final Collection<DataSource> ds) { + dataSources.clear(); + dataSources.addAll(ds); + } +} Added: tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java?rev=1475982&view=auto ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java (added) +++ tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java Thu Apr 25 21:54:08 2013 @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.resource.jdbc; + +import org.apache.openejb.junit.ApplicationComposer; +import org.apache.openejb.resource.jdbc.router.FailOverRouter; +import org.apache.openejb.testing.Configuration; +import org.apache.openejb.testing.Module; +import org.apache.openejb.testng.PropertiesBuilder; +import org.apache.xbean.finder.AnnotationFinder; +import org.apache.xbean.finder.IAnnotationFinder; +import org.apache.xbean.finder.archive.ClassesArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(ApplicationComposer.class) +public class FailOverRouterTest { + @Resource(name = "routedDs") + private DataSource failover; + + @Resource(name = "router") + private FailOverRouter router; + + @Test + public void test() throws SQLException { + assertNotNull(failover); + + int total = 0; + int i = 1; + for (int it = 0; it < 6; it++) { + for (int a = 0; a < 10; a++) { + total++; + assertEquals("jdbc:hsqldb:mem:fo" + i, url(failover.getConnection())); + } + + rotate(); + i = 1 + (i % 3); + } + + assertEquals(60, total); + } + + private void rotate() { + final Iterator<DataSource> it = router.getDataSources().iterator(); + final DataSource ds1 = it.next(); + router.updateDataSources(Arrays.asList(it.next(), it.next(), ds1)); + } + + @Configuration + public Properties configuration() { + // datasources + return datasource(datasource(datasource(new PropertiesBuilder(), "fo1"), "fo2"), "fo3") + + // router + .property("router", "new://Resource?class-name=" + FailOverRouter.class.getName()) + .property("router.datasourceNames", "fo1,fo2,fo3") + + // routed DS + .property("routedDs", "new://Resource?provider=RoutedDataSource&type=DataSource") + .property("routedDs.router", "router") + + .build(); + } + + private static String url(final Connection c) throws SQLException { + final DatabaseMetaData dmd = c.getMetaData(); + try { + return dmd.getURL(); + } finally { + c.close(); + } + } + + private PropertiesBuilder datasource(final PropertiesBuilder propertiesBuilder, final String name) { + return propertiesBuilder + .property(name, "new://Resource?type=DataSource") + .property(name + ".JdbcUrl", "jdbc:hsqldb:mem:" + name); + } + + @Module + public IAnnotationFinder finder() { // needed to run the test + return new AnnotationFinder(new ClassesArchive()); + } +}