You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2013/04/26 08:46:02 UTC

svn commit: r1476067 - in /tomee/tomee/trunk/container/openejb-core/src: main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java test/java/org/apache/openejb/resource/jdbc/ReverseFailOverRouterTest.java

Author: rmannibucau
Date: Fri Apr 26 06:46:01 2013
New Revision: 1476067

URL: http://svn.apache.org/r1476067
Log:
TOMEE-912 adding basic strategies to failover router

Added:
    tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ReverseFailOverRouterTest.java
      - copied, changed from r1476045, 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/resource/jdbc/router/FailOverRouter.java

Modified: 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=1476067&r1=1476066&r2=1476067&view=diff
==============================================================================
--- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java (original)
+++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/router/FailOverRouter.java Fri Apr 26 06:46:01 2013
@@ -26,16 +26,26 @@ import java.lang.reflect.InvocationHandl
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 
 public class FailOverRouter extends AbstractRouter {
     private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_SERVER, FailOverRouter.class);
+
+    public static final String DEFAULT_STRATEGY = "default";
+
     private final AtomicReference<DataSource> facade = new AtomicReference<DataSource>();
     private final Collection<DataSource> dataSources = new CopyOnWriteArrayList<DataSource>();
+
     private String delimiter = ",";
+    private String strategy = DEFAULT_STRATEGY;
     private String datasourceNames = "";
 
     @Override
@@ -53,6 +63,15 @@ public class FailOverRouter extends Abst
         initDataSources();
     }
 
+    public void setStrategy(final String strategy) {
+        if (strategy == null) {
+            this.strategy = DEFAULT_STRATEGY;
+        } else {
+            this.strategy = strategy.toLowerCase(Locale.ENGLISH).trim();
+        }
+        initFacade();
+    }
+
     private void initDataSources() {
         dataSources.clear();
         for (final String ds : datasourceNames.split(Pattern.quote(delimiter))) {
@@ -84,7 +103,7 @@ public class FailOverRouter extends Abst
 
         facade.set(DataSource.class.cast(Proxy.newProxyInstance(
                 Thread.currentThread().getContextClassLoader(),
-                new Class<?>[]{ clazz }, new FacadeHandler(dataSources))));
+                new Class<?>[]{ clazz }, new FacadeHandler(dataSources, strategy))));
     }
 
     public Collection<DataSource> getDataSources() {
@@ -99,9 +118,12 @@ public class FailOverRouter extends Abst
 
     private static class FacadeHandler implements InvocationHandler {
         private final Collection<DataSource> delegates;
+        private final String strategy;
+        private final AtomicInteger currentIdx = new AtomicInteger(0); // used by some strategies
 
-        public FacadeHandler(final Collection<DataSource> dataSources) {
+        public FacadeHandler(final Collection<DataSource> dataSources, final String strategy) {
             this.delegates = dataSources;
+            this.strategy = strategy;
         }
 
         @Override
@@ -119,7 +141,8 @@ public class FailOverRouter extends Abst
             }
 
             int ex = 0;
-            for (final DataSource ds : delegates) {
+            final Collection<DataSource> sources = sortFollowingStrategy(strategy, delegates, currentIdx);
+            for (final DataSource ds : sources) {
                 try {
                     if (method.getName().startsWith("set")) {
                         method.invoke(ds, args);
@@ -128,7 +151,7 @@ public class FailOverRouter extends Abst
                     }
                 } catch (final InvocationTargetException ite) {
                     ex++;
-                    if (ex == delegates.size()) { // all failed so throw the exception
+                    if (ex == sources.size()) { // all failed so throw the exception
                         throw ite.getCause();
                     }
                 }
@@ -137,4 +160,54 @@ public class FailOverRouter extends Abst
             return null;
         }
     }
+
+    private static Collection<DataSource> sortFollowingStrategy(final String strategy, final Collection<DataSource> delegates, final AtomicInteger idx) {
+        if (strategy == null) {
+            return delegates;
+        }
+
+        if (DEFAULT_STRATEGY.equals(strategy) || strategy.isEmpty()) {
+            return delegates;
+        }
+
+        //
+        // take care next strategies can break multiple calls on the facade
+        // it is only intended to be used for connection selection
+        //
+
+        if ("random".equals(strategy)) {
+            final List<DataSource> ds = new ArrayList<DataSource>(delegates);
+            Collections.shuffle(ds);
+            return ds;
+        }
+
+        if ("reverse".equals(strategy)) {
+            final List<DataSource> ds = new ArrayList<DataSource>(delegates);
+            final int times = idx.incrementAndGet() % ds.size();
+            for (int i = 0; i < times; i++) {
+                Collections.reverse(ds);
+            }
+            return ds;
+        }
+
+        if (strategy.startsWith("round-robin")) {
+            final int step;
+            if (strategy.contains("%")) {
+                step = Math.max(1, Integer.parseInt(strategy.substring(strategy.lastIndexOf("%") + 1)));
+            } else {
+                step = 1;
+            }
+
+            final List<DataSource> ds = new ArrayList<DataSource>(delegates);
+
+            int currentIdx = 0;
+            for (int i = 0; i < step; i++) {
+                currentIdx = idx.incrementAndGet();
+            }
+            Collections.rotate(ds, 1 + (currentIdx % ds.size()));
+            return ds;
+        }
+
+        return delegates;
+    }
 }

Copied: tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ReverseFailOverRouterTest.java (from r1476045, 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/ReverseFailOverRouterTest.java?p2=tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ReverseFailOverRouterTest.java&p1=tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java&r1=1476045&r2=1476067&rev=1476067&view=diff
==============================================================================
--- tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/FailOverRouterTest.java (original)
+++ tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/ReverseFailOverRouterTest.java Fri Apr 26 06:46:01 2013
@@ -32,56 +32,33 @@ 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 {
+public class ReverseFailOverRouterTest {
     @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;
+        int i = 2;
         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("Iteration #" + i, "jdbc:hsqldb:mem:fo" + i, url(failover.getConnection()));
+            i = 1 + (i % 2);
         }
-
-        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")
+        return datasource(datasource(new PropertiesBuilder(), "fo1"), "fo2")
 
-                // router
                 .property("router", "new://Resource?class-name=" + FailOverRouter.class.getName())
-                .property("router.datasourceNames", "fo1,fo2,fo3")
+                .property("router.datasourceNames", "fo1,fo2")
+                .property("router.strategy", "reverse")
 
-                // routed DS
                 .property("routedDs", "new://Resource?provider=RoutedDataSource&type=DataSource")
                 .property("routedDs.router", "router")