You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2012/03/23 07:18:27 UTC

svn commit: r1304198 [1/3] - in /openejb/trunk/openejb: itests/failover/src/main/java/org/apache/openejb/server/control/ itests/failover/src/test/java/org/apache/openejb/itest/failover/ server/openejb-client/ server/openejb-client/src/main/java/org/apa...

Author: dblevins
Date: Fri Mar 23 06:18:25 2012
New Revision: 1304198

URL: http://svn.apache.org/viewvc?rev=1304198&view=rev
Log:
OPENEJB-1801	Improved StickyConnectionStrategy for clustering and failover
OPENEJB-1802	Improved RoundRobinConnectionStrategy for clustering and failover
OPENEJB-1803	Improved RandomConnectionStrategy for clustering and failover
OPENEJB-1804	Client Event API for monitoring connection, clustering and failover activity
OPENEJB-1805	All client logging revised and greatly expanded
OPENEJB-1806	New "sticky+random" ConnectionStrategy
OPENEJB-1807	New "sticky+round" ConnectionStrategy
OPENEJB-1808	Client property 'openejb.client.connection.strategy' can be set as System or InitialContext property
OPENEJB-1809	RandomConnectionStrategy fixed reliability issues
OPENEJB-1810	Client Event issued when Servers Added/Removed from cluster
OPENEJB-1811	Client Event issued on connection Failover and Request Retry
OPENEJB-1812	Client Event issued on Configuration change

Many more events than what is listed above.  Ideally we'd javadoc them all

Added:
    openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/Context.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ClientThread.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RandomConnectionStrategyTest.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RoundRobinConnectionStrategyTest.java
      - copied, changed from r1302856, openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/SpeedTest.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/StickyConnectionStrategyTest.java
      - copied, changed from r1302856, openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/DedicatedRootServerTest.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/AbstractConnectionStrategy.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Context.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EventLogger.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Observers.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/BootstrappingConnection.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ClientVersion.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ClusterMetaDataUpdated.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionFactoryAdded.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionFactoryRemoved.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionFailed.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionOpened.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionStrategyAdded.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionStrategyFailed.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ConnectionStrategyRemoved.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/FailoverSelection.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/Log.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ObserverAdded.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ObserverRemoved.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/Observes.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RandomFailoverSelection.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RemoteInitialContextCreated.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RequestFailed.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RetryConditionAdded.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RetryConditionRemoved.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RetryingRequest.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/RoundRobinFailoverSelection.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ServerAdded.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/ServerRemoved.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/event/StickyFailoverSelection.java
    openejb/trunk/openejb/server/openejb-client/src/main/resources/openejb-client-version.properties
    openejb/trunk/openejb/server/openejb-client/src/test/java/org/apache/openejb/client/RandomConnectionStrategyIteratorTest.java
    openejb/trunk/openejb/server/openejb-client/src/test/java/org/apache/openejb/client/RoundRobinConnectionStrategyIteratorTest.java
    openejb/trunk/openejb/server/openejb-client/src/test/java/org/apache/openejb/client/StickyConnectionStrategyIteratorTest.java
    openejb/trunk/openejb/server/openejb-ejbd/src/test/java/org/apache/openejb/server/ejbd/StaticFailoverTest.java
      - copied, changed from r1302856, openejb/trunk/openejb/server/openejb-ejbd/src/test/java/org/apache/openejb/server/ejbd/FullPoolFailoverTest.java
Removed:
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/DedicatedRootServerTest.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/SpeedTest.java
    openejb/trunk/openejb/server/openejb-client/src/test/java/org/apache/openejb/client/StickyConnectionStrategyTest.java
Modified:
    openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/StandaloneServer.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/FailoverTest.java
    openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ReconnectDelayTest.java
    openejb/trunk/openejb/server/openejb-client/pom.xml
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ClusterMetaData.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ConnectionManager.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/RandomConnectionStrategy.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/RoundRobinConnectionStrategy.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/SocketConnectionFactory.java
    openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/StickyConnectionStrategy.java
    openejb/trunk/openejb/server/openejb-ejbd/src/test/java/org/apache/openejb/server/ejbd/FullPoolFailoverTest.java
    openejb/trunk/openejb/server/openejb-multicast/src/main/java/org/apache/openejb/server/discovery/Tracker.java

Added: openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/Context.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/Context.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/Context.java (added)
+++ openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/Context.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,42 @@
+/*
+ * 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.server.control;
+
+import java.util.HashMap;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class Context {
+
+    private final HashMap<Class, Object> components = new HashMap<Class, Object>();
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T get(Class<T> type) {
+        return (T) components.get(type);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T set(Class<T> type, T component) {
+        return (T) components.put(type, component);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T remove(Class<T> type) {
+        return (T) components.remove(type);
+    }
+}

Modified: openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/StandaloneServer.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/StandaloneServer.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/StandaloneServer.java (original)
+++ openejb/trunk/openejb/itests/failover/src/main/java/org/apache/openejb/server/control/StandaloneServer.java Fri Mar 23 06:18:25 2012
@@ -56,6 +56,7 @@ public class StandaloneServer {
     private boolean verbose = false;
     private OutputStream out = System.out;
     private Options options = new Options(properties);
+    private Context context = new Context();
 
     public StandaloneServer(File home) {
         this(home, home);
@@ -78,6 +79,17 @@ public class StandaloneServer {
         jvmOpts.add("-javaagent:" + javaagentJar.getAbsolutePath());
     }
 
+    /**
+     * Used as a convenience for tracking objects associated
+     * with this server.  Does not affect the running server
+     * and none of these objects are in any way sent or part
+     * of the server itself.
+     * @return
+     */
+    public Context getContext() {
+        return context;
+    }
+
     public ServerService getServerService(String string) {
         return new ServerService(string);
     }
@@ -135,6 +147,10 @@ public class StandaloneServer {
             return this;
         }
 
+        public Object get(String name) {
+            return properties.get(this.name + "." + name);
+        }
+
         public ServerService threads(int threads) {
             setThreads(threads);
             return this;

Added: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ClientThread.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ClientThread.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ClientThread.java (added)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ClientThread.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,79 @@
+/*
+ * 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.itest.failover;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+* @version $Rev$ $Date$
+*/
+public class ClientThread implements Runnable {
+
+    private final AtomicBoolean run = new AtomicBoolean(false);
+    private final AtomicLong delay = new AtomicLong(0);
+    private final Callable callable;
+
+    public ClientThread(Callable callable) {
+        this.callable = callable;
+    }
+
+    @Override
+    public void run() {
+        while (run.get()) {
+            pause();
+
+            try {
+                callable.call();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public ClientThread delay(long delay){
+        setDelay(delay);
+        return this;
+    }
+
+    public void setDelay(long delay) {
+        this.delay.set(delay);
+    }
+
+    private void pause() {
+        final long l = delay.get();
+        try {
+            if (l > 0) Thread.sleep(l);
+        } catch (InterruptedException e) {
+            Thread.interrupted();
+        }
+    }
+
+    public ClientThread start() {
+        run.set(true);
+        final Thread thread = new Thread(this);
+        thread.setDaemon(true);
+        thread.start();
+        return this;
+    }
+
+    public ClientThread stop() {
+        run.set(false);
+        return this;
+    }
+}

Modified: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/FailoverTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/FailoverTest.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/FailoverTest.java (original)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/FailoverTest.java Fri Mar 23 06:18:25 2012
@@ -20,13 +20,14 @@ import org.apache.openejb.itest.failover
 import org.apache.openejb.loader.Files;
 import org.apache.openejb.loader.Zips;
 import org.apache.openejb.server.control.StandaloneServer;
-import org.apache.openejb.util.NetworkUtil;
 import org.junit.Assert;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
+import static org.apache.openejb.util.NetworkUtil.getNextAvailablePort;
+
 /**
  * @version $Rev$ $Date$
  */
@@ -74,13 +75,10 @@ public class FailoverTest {
 
         final StandaloneServer.ServerService multipoint = server.getServerService("multipoint");
         multipoint.setBind("localhost");
-        multipoint.setPort(NetworkUtil.getNextAvailablePort());
+        multipoint.setPort(getNextAvailablePort());
         multipoint.setEnabled(true);
         multipoint.set("discoveryName", name);
         return server;
     }
 
-    public int getAvailablePort() {
-        return NetworkUtil.getNextAvailablePort();
-    }
 }

Added: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RandomConnectionStrategyTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RandomConnectionStrategyTest.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RandomConnectionStrategyTest.java (added)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RandomConnectionStrategyTest.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,330 @@
+/*
+ * 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.itest.failover;
+
+import org.apache.openejb.client.Client;
+import org.apache.openejb.client.RemoteInitialContextFactory;
+import org.apache.openejb.client.event.ClusterMetaDataUpdated;
+import org.apache.openejb.client.event.Observes;
+import org.apache.openejb.itest.failover.ejb.Calculator;
+import org.apache.openejb.loader.Files;
+import org.apache.openejb.loader.IO;
+import org.apache.openejb.loader.Zips;
+import org.apache.openejb.server.control.StandaloneServer;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.ejb.EJBException;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.apache.openejb.util.NetworkUtil.getNextAvailablePort;
+
+public class RandomConnectionStrategyTest {
+
+    static final Logger logger = Logger.getLogger("org.apache.openejb.client");
+
+    static {
+        final ConsoleHandler consoleHandler = new ConsoleHandler();
+        consoleHandler.setLevel(Level.FINER);
+        logger.addHandler(consoleHandler);
+        logger.setLevel(Level.FINER);
+        logger.setUseParentHandlers(false);
+    }
+
+    @Test
+    public void test() throws Exception {
+
+//        To run in an IDE, uncomment and update this line
+//        System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
+        System.setProperty("openejb.client.connection.strategy", "roundrobin");
+
+        final File zip = Repository.getArtifact("org.apache.openejb", "openejb-standalone", "zip");
+        final File app = Repository.getArtifact("org.apache.openejb.itests", "failover-ejb", "jar");
+
+        final File dir = Files.tmpdir();
+
+        final StandaloneServer root;
+        {
+            final String name = "root";
+            final File home = new File(dir, name);
+
+            Files.mkdir(home);
+            Zips.unzip(zip, home, true);
+
+            root = new StandaloneServer(home, home);
+            root.killOnExit();
+            root.ignoreOut();
+            root.setProperty("name", name);
+            root.setProperty("openejb.extract.configuration", "false");
+
+            final StandaloneServer.ServerService multipoint = root.getServerService("multipoint");
+            multipoint.setBind("localhost");
+            multipoint.setPort(getNextAvailablePort());
+            multipoint.setDisabled(false);
+            multipoint.set("discoveryName", name);
+
+            logger.info("Starting Root server");
+            root.start();
+        }
+
+        final Services services = new Services();
+        Client.addEventObserver(services);
+
+        final Map<String, StandaloneServer> servers = new HashMap<String, StandaloneServer>();
+        for (String name : new String[]{"red", "green", "blue"}) {
+
+            final File home = new File(dir, name);
+            Files.mkdir(home);
+            Zips.unzip(zip, home, true);
+
+            final StandaloneServer server = new StandaloneServer(home, home);
+            server.killOnExit();
+            server.ignoreOut();
+            server.setProperty("name", name);
+            server.setProperty("openejb.extract.configuration", "false");
+
+            IO.copy(app, Files.path(home, "apps", "itest.jar"));
+            IO.copy(IO.read("<openejb><Deployments dir=\"apps/\"/></openejb>"), Files.path(home, "conf", "openejb.xml"));
+
+            final StandaloneServer.ServerService ejbd = server.getServerService("ejbd");
+            ejbd.setBind("localhost");
+            ejbd.setDisabled(false);
+            ejbd.setPort(getNextAvailablePort());
+            ejbd.setThreads(5);
+
+            final URI uri = URI.create(String.format("ejbd://%s:%s/%s", ejbd.getBind(), ejbd.getPort(), name));
+            ejbd.set("discovery", "ejb:" + uri);
+            services.add(uri);
+            server.getContext().set(URI.class, uri);
+
+            final StandaloneServer.ServerService multipoint = server.getServerService("multipoint");
+            multipoint.setPort(getNextAvailablePort());
+            multipoint.setDisabled(false);
+            multipoint.set("discoveryName", name);
+            multipoint.set("initialServers", "localhost:" + root.getServerService("multipoint").getPort());
+
+            servers.put(name, server);
+
+            logger.info(String.format("Starting %s server", name));
+
+            server.start(1, TimeUnit.MINUTES);
+        }
+
+
+        System.setProperty("openejb.client.requestretry", "true");
+        System.setProperty("openejb.client.connection.strategy", "random");
+
+        logger.info("Beginning Test");
+
+        final Properties environment = new Properties();
+        environment.put(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName());
+        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + servers.values().iterator().next().getServerService("ejbd").getPort() + "/provider");
+
+        final InitialContext context = new InitialContext(environment);
+        final Calculator bean = (Calculator) context.lookup("CalculatorBeanRemote");
+
+        for (Map.Entry<String, StandaloneServer> entry : servers.entrySet()) {
+            final String name = entry.getKey();
+            final StandaloneServer server = entry.getValue();
+            final URI serverURI = server.getContext().get(URI.class);
+
+            logger.info("Waiting for updated list");
+            services.assertServices(10, TimeUnit.SECONDS, new CalculatorCallable(bean), 500);
+
+            logger.info("Asserting balance");
+            assertBalance(bean, services.get().size());
+
+            logger.info("Shutting down " + name);
+            server.kill();
+            services.remove(serverURI);
+        }
+
+        logger.info("All Servers Shutdown");
+
+        try {
+            logger.info("Making one last request, expecting complete failover");
+
+            final String name = bean.name();
+            Assert.fail("Server should be destroyed: " + name);
+        } catch (EJBException e) {
+            logger.info(String.format("Pass.  Request resulted in %s: %s", e.getCause().getClass().getSimpleName(), e.getMessage()));
+            // good
+        }
+
+
+        for (Map.Entry<String, StandaloneServer> entry : servers.entrySet()) {
+            final String name = entry.getKey();
+            final StandaloneServer server = entry.getValue();
+            final URI serverURI = server.getContext().get(URI.class);
+
+            logger.info(String.format("Starting %s server", name));
+
+            server.start(1, TimeUnit.MINUTES);
+            services.add(serverURI);
+
+            logger.info("Waiting for updated list");
+            services.assertServices(10, TimeUnit.SECONDS, new CalculatorCallable(bean), 500);
+
+            logger.info("Asserting balance");
+            assertBalance(bean, services.get().size());
+        }
+    }
+
+    private void assertBalance(Calculator bean, int size) {
+        final int expectedInvocations = 1000;
+        final double percent = 0.10;
+        final int totalInvocations = size * expectedInvocations;
+
+
+        // Verify the work reached all servers
+        Set<Map.Entry<String, AtomicInteger>> entries = invoke(bean, totalInvocations).entrySet();
+
+        Assert.assertEquals(size, entries.size());
+
+        // And each server got a minimum of %10 percent of the traffic
+        for (Map.Entry<String, AtomicInteger> entry : entries) {
+
+            final int actualInvocations = entry.getValue().get();
+
+            Assert.assertTrue(String.format("%s out of %s is too low", actualInvocations, expectedInvocations), actualInvocations > expectedInvocations * percent );
+        }
+    }
+
+
+    private Map<String, AtomicInteger> invoke(Calculator bean, int max) {
+        final Map<String, AtomicInteger> invocations = new HashMap<String, AtomicInteger>();
+        for (int i = 0; i < max; i++) {
+            final String name = bean.name();
+
+            if (!invocations.containsKey(name)) {
+                invocations.put(name, new AtomicInteger());
+            }
+
+            invocations.get(name).incrementAndGet();
+        }
+
+        for (Map.Entry<String, AtomicInteger> entry : invocations.entrySet()) {
+            logger.info(String.format("Server %s invoked %s times", entry.getKey(), entry.getValue()));
+        }
+
+        return invocations;
+    }
+
+    public static class Services {
+        static final Logger logger = Logger.getLogger(Services.class.getName());
+
+        private final ReentrantLock lock = new ReentrantLock();
+        private final Condition condition = lock.newCondition();
+
+        private final Set<URI> expected = new HashSet<URI>();
+
+        public Services() {
+        }
+
+        public Set<URI> get() {
+            return expected;
+        }
+
+        public boolean add(URI uri) {
+            return expected.add(uri);
+        }
+
+        public boolean remove(URI o) {
+            return expected.remove(o);
+        }
+
+        public void observe(@Observes ClusterMetaDataUpdated updated) {
+            final URI[] locations = updated.getClusterMetaData().getLocations();
+            final Set<URI> found = new HashSet<URI>(Arrays.asList(locations));
+
+            if (expected.equals(found)) {
+                lock.lock();
+                try {
+                    condition.signal();
+                } finally {
+                    lock.unlock();
+                }
+            }
+        }
+
+        public Set<URI> diff(Set<URI> a, Set<URI> b) {
+            final Set<URI> diffs = new HashSet<URI>();
+            for (URI uri : b) {
+                if (!a.contains(uri)) diffs.add(uri);
+            }
+
+            return diffs;
+        }
+
+        public void assertServices(long timeout, TimeUnit unit, Callable callable) {
+            assertServices(timeout, unit, callable, 10);
+        }
+
+        public void assertServices(long timeout, TimeUnit unit, Callable callable, int delay) {
+            final ClientThread client = new ClientThread(callable);
+            client.delay(delay);
+            client.start();
+            try {
+                Assert.assertTrue(String.format("services failed to come online: waited %s %s", timeout, unit), await(timeout, unit));
+            } catch (InterruptedException e) {
+                Thread.interrupted();
+                Assert.fail("Interrupted");
+            } finally {
+                client.stop();
+            }
+        }
+
+        public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
+            lock.lock();
+            try {
+                return condition.await(timeout, unit);
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    private static class CalculatorCallable implements Callable {
+        private final Calculator bean;
+
+        public CalculatorCallable(Calculator bean) {
+            this.bean = bean;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            return bean.name();
+        }
+    }
+}

Modified: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ReconnectDelayTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ReconnectDelayTest.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ReconnectDelayTest.java (original)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/ReconnectDelayTest.java Fri Mar 23 06:18:25 2012
@@ -52,7 +52,7 @@ public class ReconnectDelayTest {
     public void test() throws Exception {
 
         // To run in an IDE, uncomment and update this line
-        //System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
+        System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
 
         Duration reconnectDelay = new Duration("1 second");
 

Copied: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RoundRobinConnectionStrategyTest.java (from r1302856, openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/SpeedTest.java)
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RoundRobinConnectionStrategyTest.java?p2=openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RoundRobinConnectionStrategyTest.java&p1=openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/SpeedTest.java&r1=1302856&r2=1304198&rev=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/SpeedTest.java (original)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/RoundRobinConnectionStrategyTest.java Fri Mar 23 06:18:25 2012
@@ -16,7 +16,10 @@
  */
 package org.apache.openejb.itest.failover;
 
+import org.apache.openejb.client.Client;
 import org.apache.openejb.client.RemoteInitialContextFactory;
+import org.apache.openejb.client.event.ClusterMetaDataUpdated;
+import org.apache.openejb.client.event.Observes;
 import org.apache.openejb.itest.failover.ejb.Calculator;
 import org.apache.openejb.loader.Files;
 import org.apache.openejb.loader.IO;
@@ -29,32 +32,77 @@ import javax.ejb.EJBException;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
-
-public class SpeedTest extends FailoverTest {
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.apache.openejb.util.NetworkUtil.getNextAvailablePort;
+
+public class RoundRobinConnectionStrategyTest {
+
+    static final Logger logger = Logger.getLogger("org.apache.openejb.client");
+
+    static {
+        final ConsoleHandler consoleHandler = new ConsoleHandler();
+        consoleHandler.setLevel(Level.FINER);
+        logger.addHandler(consoleHandler);
+        logger.setLevel(Level.FINER);
+        logger.setUseParentHandlers(false);
+    }
 
     @Test
     public void test() throws Exception {
 
-        // To run in an IDE, uncomment and update this line
-        //System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
+//        To run in an IDE, uncomment and update this line
+//        System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
+        System.setProperty("openejb.client.connection.strategy", "roundrobin");
 
         final File zip = Repository.getArtifact("org.apache.openejb", "openejb-standalone", "zip");
         final File app = Repository.getArtifact("org.apache.openejb.itests", "failover-ejb", "jar");
 
         final File dir = Files.tmpdir();
 
-        final StandaloneServer root = createMultipointServer(zip, dir, "root");
-        root.setOut(System.out);
-        root.start(1, TimeUnit.MINUTES);
+        final StandaloneServer root;
+        {
+            final String name = "root";
+            final File home = new File(dir, name);
+
+            Files.mkdir(home);
+            Zips.unzip(zip, home, true);
+
+            root = new StandaloneServer(home, home);
+            root.killOnExit();
+            root.ignoreOut();
+            root.setProperty("name", name);
+            root.setProperty("openejb.extract.configuration", "false");
+
+            final StandaloneServer.ServerService multipoint = root.getServerService("multipoint");
+            multipoint.setBind("localhost");
+            multipoint.setPort(getNextAvailablePort());
+            multipoint.setDisabled(false);
+            multipoint.set("discoveryName", name);
+
+            logger.info("Starting Root server");
+            root.start();
+        }
 
-        final List<StandaloneServer> servers = new ArrayList<StandaloneServer>();
+        final Services services = new Services();
+        Client.addEventObserver(services);
 
-        for (String name : new String[]{"red", "green", "blue", "yellow", "orange"}) {
+        final Map<String, StandaloneServer> servers = new HashMap<String, StandaloneServer>();
+        for (String name : new String[]{"red", "green", "blue"}) {
 
             final File home = new File(dir, name);
             Files.mkdir(home);
@@ -70,64 +118,208 @@ public class SpeedTest extends FailoverT
             IO.copy(IO.read("<openejb><Deployments dir=\"apps/\"/></openejb>"), Files.path(home, "conf", "openejb.xml"));
 
             final StandaloneServer.ServerService ejbd = server.getServerService("ejbd");
+            ejbd.setBind("localhost");
             ejbd.setDisabled(false);
-            ejbd.setPort(getAvailablePort());
+            ejbd.setPort(getNextAvailablePort());
             ejbd.setThreads(5);
 
+            final URI uri = URI.create(String.format("ejbd://%s:%s/%s", ejbd.getBind(), ejbd.getPort(), name));
+            ejbd.set("discovery", "ejb:" + uri);
+            services.add(uri);
+            server.getContext().set(URI.class, uri);
+
             final StandaloneServer.ServerService multipoint = server.getServerService("multipoint");
-            multipoint.setPort(getAvailablePort());
+            multipoint.setPort(getNextAvailablePort());
             multipoint.setDisabled(false);
             multipoint.set("discoveryName", name);
-            multipoint.set("initialServers", "localhost:"+root.getServerService("multipoint").getPort());
+            multipoint.set("initialServers", "localhost:" + root.getServerService("multipoint").getPort());
 
-            server.start(1, TimeUnit.MINUTES);
+            servers.put(name, server);
+
+            logger.info(String.format("Starting %s server", name));
 
-            servers.add(server);
+            server.start(1, TimeUnit.MINUTES);
         }
 
-        Collections.reverse(servers);
 
         System.setProperty("openejb.client.requestretry", "true");
+        System.setProperty("openejb.client.connection.strategy", "roundrobin");
+
+        logger.info("Beginning Test");
 
         final Properties environment = new Properties();
         environment.put(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName());
-        environment.put(Context.PROVIDER_URL, "failover:ejbd://localhost:" + servers.get(0).getServerService("ejbd").getPort());
+        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + servers.values().iterator().next().getServerService("ejbd").getPort() + "/provider");
 
         final InitialContext context = new InitialContext(environment);
         final Calculator bean = (Calculator) context.lookup("CalculatorBeanRemote");
 
-        // Hotspotting doesn't kick in with just a few invocations
-        // Later we'll be doing thousands of invocations and that will
-        // bring the invoke time down significantly
-
-        // With this test we're looking for issues with failed servers
-        // causing delays even though they should have been removed from
-        // the clients invocation list
-
-        final long slowest = invoke(bean, 5);
-        System.out.printf("Base invocation speed: %s microseconds", slowest);
-        System.out.println();
-
-        for (StandaloneServer server : servers) {
-            final long speed = invoke(bean, 10000);
-            Assert.assertTrue(String.format("Average invocation time %s microseconds higher than pre-hotspot time of %s microseconds", speed, slowest), speed < slowest);
+        for (Map.Entry<String, StandaloneServer> entry : servers.entrySet()) {
+            final String name = entry.getKey();
+            final StandaloneServer server = entry.getValue();
+            final URI serverURI = server.getContext().get(URI.class);
+
+            logger.info("Waiting for updated list");
+            services.assertServices(10, TimeUnit.SECONDS, new CalculatorCallable(bean), 500);
+
+            logger.info("Asserting balance");
+            assertBalance(bean, services.get().size());
+
+            logger.info("Shutting down " + name);
             server.kill();
+            services.remove(serverURI);
         }
 
-        System.out.println("All servers destroyed");
+        logger.info("All Servers Shutdown");
 
         try {
-            System.out.println(String.format("Average invocation time %s microseconds", invoke(bean, 10000)));
-            Assert.fail("Server should be destroyed");
+            logger.info("Making one last request, expecting complete failover");
+
+            final String name = bean.name();
+            Assert.fail("Server should be destroyed: " + name);
         } catch (EJBException e) {
+            logger.info(String.format("Pass.  Request resulted in %s: %s", e.getCause().getClass().getSimpleName(), e.getMessage()));
             // good
         }
 
-        final StandaloneServer server = servers.get(0);
-        server.start(1, TimeUnit.MINUTES);
 
-        final long speed = invoke(bean, 10000);
-        Assert.assertTrue(String.format("Average invocation time %s microseconds higher than pre-hotspot time of %s microseconds", speed, slowest), speed < slowest);
+        for (Map.Entry<String, StandaloneServer> entry : servers.entrySet()) {
+            final String name = entry.getKey();
+            final StandaloneServer server = entry.getValue();
+            final URI serverURI = server.getContext().get(URI.class);
+
+            logger.info(String.format("Starting %s server", name));
+
+            server.start(1, TimeUnit.MINUTES);
+            services.add(serverURI);
+
+            logger.info("Waiting for updated list");
+            services.assertServices(10, TimeUnit.SECONDS, new CalculatorCallable(bean), 500);
+
+            logger.info("Asserting balance");
+            assertBalance(bean, services.get().size());
+        }
+    }
+
+    private void assertBalance(Calculator bean, int size) {
+        final int expectedInvocations = 100;
+        final int tolerance = 2;
+        final int totalInvocations = size * expectedInvocations + (size * tolerance);
+
+
+        // Verify the work was split up more or less evenly (within a tolerance of 2)
+        for (Map.Entry<String, AtomicInteger> entry : invoke(bean, totalInvocations).entrySet()) {
+
+            final int actualInvocations = entry.getValue().get();
+
+            Assert.assertTrue(String.format("Expected %s invocations, received %s", expectedInvocations, actualInvocations), actualInvocations >= expectedInvocations && actualInvocations <= (expectedInvocations + tolerance + size) );
+        }
+    }
+
 
+    private Map<String, AtomicInteger> invoke(Calculator bean, int max) {
+        final Map<String, AtomicInteger> invocations = new HashMap<String, AtomicInteger>();
+        for (int i = 0; i < max; i++) {
+            final String name = bean.name();
+
+            if (!invocations.containsKey(name)) {
+                invocations.put(name, new AtomicInteger());
+            }
+
+            invocations.get(name).incrementAndGet();
+        }
+
+        for (Map.Entry<String, AtomicInteger> entry : invocations.entrySet()) {
+            logger.info(String.format("Server %s invoked %s times", entry.getKey(), entry.getValue()));
+        }
+
+        return invocations;
+    }
+
+    public static class Services {
+        static final Logger logger = Logger.getLogger(Services.class.getName());
+
+        private final ReentrantLock lock = new ReentrantLock();
+        private final Condition condition = lock.newCondition();
+
+        private final Set<URI> expected = new HashSet<URI>();
+
+        public Services() {
+        }
+
+        public Set<URI> get() {
+            return expected;
+        }
+
+        public boolean add(URI uri) {
+            return expected.add(uri);
+        }
+
+        public boolean remove(URI o) {
+            return expected.remove(o);
+        }
+
+        public void observe(@Observes ClusterMetaDataUpdated updated) {
+            final URI[] locations = updated.getClusterMetaData().getLocations();
+            final Set<URI> found = new HashSet<URI>(Arrays.asList(locations));
+
+            if (expected.equals(found)) {
+                lock.lock();
+                try {
+                    condition.signal();
+                } finally {
+                    lock.unlock();
+                }
+            }
+        }
+
+        public Set<URI> diff(Set<URI> a, Set<URI> b) {
+            final Set<URI> diffs = new HashSet<URI>();
+            for (URI uri : b) {
+                if (!a.contains(uri)) diffs.add(uri);
+            }
+
+            return diffs;
+        }
+
+        public void assertServices(long timeout, TimeUnit unit, Callable callable) {
+            assertServices(timeout, unit, callable, 10);
+        }
+
+        public void assertServices(long timeout, TimeUnit unit, Callable callable, int delay) {
+            final ClientThread client = new ClientThread(callable);
+            client.delay(delay);
+            client.start();
+            try {
+                Assert.assertTrue(String.format("services failed to come online: waited %s %s", timeout, unit), await(timeout, unit));
+            } catch (InterruptedException e) {
+                Thread.interrupted();
+                Assert.fail("Interrupted");
+            } finally {
+                client.stop();
+            }
+        }
+
+        public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
+            lock.lock();
+            try {
+                return condition.await(timeout, unit);
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    private static class CalculatorCallable implements Callable {
+        private final Calculator bean;
+
+        public CalculatorCallable(Calculator bean) {
+            this.bean = bean;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            return bean.name();
+        }
     }
 }

Copied: openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/StickyConnectionStrategyTest.java (from r1302856, openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/DedicatedRootServerTest.java)
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/StickyConnectionStrategyTest.java?p2=openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/StickyConnectionStrategyTest.java&p1=openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/DedicatedRootServerTest.java&r1=1302856&r2=1304198&rev=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/DedicatedRootServerTest.java (original)
+++ openejb/trunk/openejb/itests/failover/src/test/java/org/apache/openejb/itest/failover/StickyConnectionStrategyTest.java Fri Mar 23 06:18:25 2012
@@ -36,6 +36,9 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -51,13 +54,22 @@ import static org.junit.Assert.assertFal
  * service EJB requests.
  *
  */
-public class DedicatedRootServerTest {
+public class StickyConnectionStrategyTest {
+
+    static final Logger logger = Logger.getLogger("org.apache.openejb.client");
+    static {
+        final ConsoleHandler consoleHandler = new ConsoleHandler();
+        consoleHandler.setLevel(Level.FINER);
+        logger.addHandler(consoleHandler);
+        logger.setLevel(Level.FINER);
+        logger.setUseParentHandlers(false);
+    }
 
     @Test
     public void test() throws Exception {
 
         // To run in an IDE, uncomment and update this line
-        //System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
+//        System.setProperty("version", "4.0.0-beta-3-SNAPSHOT");
 
         final File zip = Repository.getArtifact("org.apache.openejb", "openejb-standalone", "zip");
         final File app = Repository.getArtifact("org.apache.openejb.itests", "failover-ejb", "jar");
@@ -86,6 +98,7 @@ public class DedicatedRootServerTest {
             multipoint.set("discoveryName", name);
             root = root1;
 
+            logger.info("Starting Root server");
             root.start();
         }
 
@@ -106,9 +119,11 @@ public class DedicatedRootServerTest {
             IO.copy(IO.read("<openejb><Deployments dir=\"apps/\"/></openejb>"), Files.path(home, "conf", "openejb.xml"));
 
             final StandaloneServer.ServerService ejbd = server.getServerService("ejbd");
+            ejbd.setBind("localhost");
             ejbd.setDisabled(false);
             ejbd.setPort(getAvailablePort());
             ejbd.setThreads(5);
+            ejbd.set("discovery", "ejb:ejbd://{bind}:{port}/" + name);
 
             final StandaloneServer.ServerService multipoint = server.getServerService("multipoint");
             multipoint.setPort(getAvailablePort());
@@ -117,6 +132,9 @@ public class DedicatedRootServerTest {
             multipoint.set("initialServers", "localhost:"+root.getServerService("multipoint").getPort());
 
             servers.put(name, server);
+
+            logger.info(String.format("Starting %s server", name));
+
             server.start(1, TimeUnit.MINUTES);
 
             invoke(name, server);
@@ -124,9 +142,11 @@ public class DedicatedRootServerTest {
 
         System.setProperty("openejb.client.requestretry", "true");
 
+        logger.info("Beginning Test");
+
         final Properties environment = new Properties();
         environment.put(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName());
-        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + servers.values().iterator().next().getServerService("ejbd").getPort());
+        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + servers.values().iterator().next().getServerService("ejbd").getPort() + "/provider");
 
         final InitialContext context = new InitialContext(environment);
         final Calculator bean = (Calculator) context.lookup("CalculatorBeanRemote");
@@ -135,9 +155,13 @@ public class DedicatedRootServerTest {
         String previous = null;
         for (StandaloneServer ignored : servers.values()) {
 
+            logger.info("Looping");
+
             // What server are we talking to now?
             final String name = bean.name();
 
+            logger.info("Sticky request to " + name);
+
             // The root should not be serving apps
             assertFalse("root".equals(name));
 
@@ -145,26 +169,43 @@ public class DedicatedRootServerTest {
             if (previous != null) assertFalse(name.equals(previous));
             previous = name;
 
+            final int i = 1000;
+
+            logger.info(String.format("Performing %s invocations, expecting %s to be used for each invocation.", i, name));
+
             // Should be the same server for the next N calls
-            invoke(bean, 1000, name);
+            invoke(bean, i, name);
+
+            logger.info("Shutting down " + name);
 
             // Now let's kill that server
             servers.get(name).kill();
         }
 
-        System.out.println("All servers destroyed");
+        logger.info("All Servers Shutdown");
 
         try {
+            logger.info("Making one last request, expecting complete failover");
+
             final String name = bean.name();
             Assert.fail("Server should be destroyed: " + name);
         } catch (EJBException e) {
+            logger.info(String.format("Pass.  Request resulted in %s: %s", e.getCause().getClass().getSimpleName(), e.getMessage()));
             // good
         }
 
+
         // Let's start a server again and invocations should now succeed
         final Iterator<StandaloneServer> iterator = servers.values().iterator();
         iterator.next();
-        iterator.next().start(1, TimeUnit.MINUTES);
+
+        final StandaloneServer server = iterator.next();
+
+        logger.info(String.format("Starting %s server", server.getProperties().get("name")));
+
+        server.start(1, TimeUnit.MINUTES);
+
+        logger.info("Performing one more invocation");
 
         assertEquals(5, bean.sum(2, 3));
     }
@@ -172,7 +213,7 @@ public class DedicatedRootServerTest {
     private void invoke(String name, StandaloneServer server) throws NamingException {
         final Properties environment = new Properties();
         environment.put(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName());
-        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + server.getServerService("ejbd").getPort());
+        environment.put(Context.PROVIDER_URL, "ejbd://localhost:" + server.getServerService("ejbd").getPort() + "/" + name);
 
         final InitialContext context = new InitialContext(environment);
         final Calculator bean = (Calculator) context.lookup("CalculatorBeanRemote");

Modified: openejb/trunk/openejb/server/openejb-client/pom.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/pom.xml?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/pom.xml (original)
+++ openejb/trunk/openejb/server/openejb-client/pom.xml Fri Mar 23 06:18:25 2012
@@ -64,6 +64,12 @@
       </openejb.osgi.export.pkg>
   </properties>
   <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
@@ -92,6 +98,30 @@
           </excludes>
         </configuration>
       </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target>
+                <tstamp>
+                  <format property="TSTAMP" pattern="hh:mm"/>
+                </tstamp>
+                <replace file="target/classes/openejb-client-version.properties"
+                         token="@DATE-REPLACED-BY-MAVEN@" value="${DSTAMP}"/>
+                <replace file="target/classes/openejb-client-version.properties"
+                         token="@TIME-REPLACED-BY-MAVEN@" value="${TSTAMP}"/>
+              </target>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
   <dependencies>

Added: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/AbstractConnectionStrategy.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/AbstractConnectionStrategy.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/AbstractConnectionStrategy.java (added)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/AbstractConnectionStrategy.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,105 @@
+/*
+ * 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.client;
+
+import org.apache.openejb.client.event.BootstrappingConnection;
+import org.apache.openejb.client.event.FailoverSelection;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractConnectionStrategy implements ConnectionStrategy {
+    public Connection connect(ClusterMetaData cluster, ServerMetaData server) throws IOException {
+        final Set<URI> failed = Client.getFailed();
+        final Set<URI> remaining = new HashSet<URI>();
+
+        boolean failover = false;
+
+        final Iterable<URI> iterable = getIterable(cluster);
+        for (URI uri : iterable) {
+            if (failed.contains(uri)) continue;
+
+            if (failover) Client.fireEvent(createFailureEvent(remaining, failed, uri));
+
+            try {
+                return connect(cluster, uri);
+            } catch (IOException e) {
+
+                if (!failover) {
+                    Collections.addAll(remaining, cluster.getLocations());
+                    remaining.removeAll(failed);
+                }
+
+                failed.add(uri);
+                remaining.remove(uri);
+                failover = true;
+            }
+        }
+
+        final URI uri = server.getLocation();
+
+        if (uri == null) throw new RemoteFailoverException("Attempted to connect to " + failed.size() + " servers.");
+
+        Client.fireEvent(new BootstrappingConnection(uri));
+
+        return connect(cluster, uri);
+    }
+
+    private Iterable<URI> getIterable(ClusterMetaData cluster) {
+        final Context context = cluster.getContext();
+        final StrategyData data = context.getComponent(StrategyData.class);
+
+        if (data != null) return data.getIterable();
+
+        context.setComponent(StrategyData.class, new StrategyData(createIterable(cluster)));
+
+        return getIterable(cluster);
+    }
+
+    protected abstract FailoverSelection createFailureEvent(Set<URI> remaining, Set<URI> failed, URI uri);
+
+    protected abstract Iterable<URI> createIterable(ClusterMetaData cluster);
+
+    protected Connection connect(ClusterMetaData cluster, URI uri) throws IOException {
+        Connection connection = ConnectionManager.getConnection(uri);
+
+        // Grabbing the URI from the associated connection allows the ConnectionFactory to
+        // employ discovery to find and connect to a server.  We then attempt to connect
+        // to the discovered server rather than repeat the discovery process again.
+        cluster.setLastLocation(connection.getURI());
+        return connection;
+    }
+
+    private static class StrategyData {
+        private final Iterable<URI> iterable;
+
+        private StrategyData(Iterable<URI> iterable) {
+            this.iterable = iterable;
+        }
+
+        public Iterable<URI> getIterable() {
+            return iterable;
+        }
+    }
+}

Modified: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java (original)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Client.java Fri Mar 23 06:18:25 2012
@@ -16,6 +16,16 @@
  */
 package org.apache.openejb.client;
 
+import org.apache.openejb.client.event.ClientVersion;
+import org.apache.openejb.client.event.ClusterMetaDataUpdated;
+import org.apache.openejb.client.event.ObserverAdded;
+import org.apache.openejb.client.event.RequestFailed;
+import org.apache.openejb.client.event.RetryConditionAdded;
+import org.apache.openejb.client.event.RetryConditionRemoved;
+import org.apache.openejb.client.event.RetryingRequest;
+import org.apache.openejb.client.event.ServerAdded;
+import org.apache.openejb.client.event.ServerRemoved;
+
 import static org.apache.openejb.client.Exceptions.newIOException;
 
 import java.io.EOFException;
@@ -26,7 +36,9 @@ import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 import java.rmi.RemoteException;
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 import java.util.HashSet;
 import java.util.List;
@@ -38,9 +50,9 @@ import java.net.URI;
 
 public class Client {
     private static final Logger logger = Logger.getLogger("OpenEJB.client");
-    private static final boolean FINEST = logger.isLoggable(Level.FINEST);
-    private static final boolean FINER = logger.isLoggable(Level.FINER);
-    private static final boolean FINE = logger.isLoggable(Level.FINE);
+    private boolean FINEST = logger.isLoggable(Level.FINEST);
+    private boolean FINER = logger.isLoggable(Level.FINER);
+    private boolean FINE = logger.isLoggable(Level.FINE);
 
     public static final ThreadLocal<Set<URI>> failed = new ThreadLocal<Set<URI>>();
 
@@ -50,21 +62,53 @@ public class Client {
     private static Client client = new Client();
     private boolean retry = false;
 
+    private final Observers observers = new Observers();
+
     public Client() {
         String retryValue = System.getProperty("openejb.client.requestretry", getRetry() + "");
         retry = Boolean.valueOf(retryValue);
+
+        observers.addObserver(new EventLogger());
+        observers.fireEvent(new ClientVersion());
+    }
+
+    public static void addEventObserver(Object observer) {
+        if (observer == null) throw new IllegalArgumentException("observer cannot be null");
+
+        if (client.observers.addObserver(observer)) {
+            fireEvent(new ObserverAdded(observer));
+        }
+    }
+
+    public static void removeEventObserver(Object observer) {
+        if (observer == null) throw new IllegalArgumentException("observer cannot be null");
+
+        if (client.observers.removeObserver(observer)) {
+            fireEvent(new ObserverAdded(observer));
+        }
+    }
+
+    public static void fireEvent(Object event) {
+        client.observers.fireEvent(event);
     }
 
     public static boolean addRetryCondition(Class<? extends Throwable> throwable) {
-        return client.retryConditions.add(throwable);
+        if (throwable == null) throw new IllegalArgumentException("throwable cannot be null");
+        final boolean add = client.retryConditions.add(throwable);
+        if (add) fireEvent(new RetryConditionAdded(throwable));
+        return add;
     }
 
     public static boolean removeRetryCondition(Class<? extends Throwable> throwable) {
-        return client.retryConditions.remove(throwable);
+        if (throwable == null) throw new IllegalArgumentException("throwable cannot be null");
+        final boolean remove = client.retryConditions.remove(throwable);
+        if (remove) fireEvent(new RetryConditionRemoved(throwable));
+        return remove;
     }
 
     // This lame hook point if only of testing
     public static void setClient(Client client) {
+        if (client == null) throw new IllegalArgumentException("client cannot be null");
         Client.client = client;
     }
 
@@ -290,13 +334,19 @@ public class Client {
             final URI uri = conn.getURI();
             final Set<URI> failed = getFailed();
 
+            Client.fireEvent(new RequestFailed(uri, req));
+
             if (FINER) {
                 logger.log(Level.FINER, "Add Failed " + uri.toString());
             }
             failed.add(uri);
             conn.discard();
+
             if (e instanceof RetryException || getRetry()) {
                 try {
+
+                    Client.fireEvent(new RetryingRequest(req, server));
+
                     processRequest(req, res, server);
                 } catch (RemoteFailoverException re) {
                     throw re;
@@ -349,36 +399,99 @@ public class Client {
         return set;
     }
 
-    private static final Map<ServerMetaData, ClusterMetaData> clusters = new ConcurrentHashMap<ServerMetaData, ClusterMetaData>();
-
     private static void setClusterMetaData(ServerMetaData server, ClusterMetaData cluster) {
+        final Context context = getContext(server);
+        context.setClusterMetaData(cluster);
+    }
+
+    private static ClusterMetaData getClusterMetaData(ServerMetaData server) {
+        return getContext(server).getClusterMetaData();
+    }
+
+    //openejb.client.connection.strategy
+
+    private boolean getRetry() {
+        return retry = Boolean.valueOf(System.getProperty("openejb.client.requestretry", retry + ""));
+    }
+
+    private static final Map<ServerMetaData, Context> contexts = new ConcurrentHashMap<ServerMetaData, Context>();
 
-        if (FINE) {
-            logger.log(Level.FINE, "Update ClusterMetaData(version=" + cluster.getVersion() + ", uris=" + cluster.getLocations().length);
+    public static Context getContext(ServerMetaData serverMetaData) {
+        Context context = contexts.get(serverMetaData);
+        if (context == null) {
+            context = new Context(serverMetaData);
+            contexts.put(serverMetaData, context);
         }
+        return context;
+    }
+
+    public static class Context {
+        private final Properties properties = new Properties();
+        private final ServerMetaData serverMetaData;
+        private ClusterMetaData clusterMetaData;
+        private Options options;
+
+        private Context(ServerMetaData serverMetaData) {
+            this.serverMetaData = serverMetaData;
+            this.clusterMetaData = new ClusterMetaData(0, serverMetaData.getLocation());
+
+            options = new Options(properties, new Options(System.getProperties()));
+        }
+
+        public ServerMetaData getServerMetaData() {
+            return serverMetaData;
+        }
+
+        public ClusterMetaData getClusterMetaData() {
+            return clusterMetaData;
+        }
+
+        public void setClusterMetaData(ClusterMetaData updated) {
+            if (updated == null) throw new IllegalArgumentException("clusterMetaData cannot be null");
+
+            ClusterMetaData previous = this.clusterMetaData;
+            this.clusterMetaData = updated;
+
+            if (updated.getConnectionStrategy() == null) {
+                updated.setConnectionStrategy(previous.getConnectionStrategy());
+            }
+            updated.setLastLocation(previous.getLastLocation());
+            final ClusterMetaDataUpdated clusterMetaDataUpdated = new ClusterMetaDataUpdated(serverMetaData, updated, clusterMetaData);
+
+            fireEvent(clusterMetaDataUpdated);
+
+            final Set<URI> found = locations(updated);
+            final Set<URI> existing = locations(previous);
 
-        if (FINER) {
-            int i = 0;
-            for (URI uri : cluster.getLocations()) {
-                final String format = String.format("ClusterMetaData(version=%s) - URI #%s %s", cluster.getVersion(), ++i, uri.toASCIIString());
-                logger.log(Level.FINER, format);
+            for (URI uri : diff(existing, found)) {
+                fireEvent(new ServerAdded(clusterMetaDataUpdated, uri));
             }
+
+            for (URI uri : diff(found, existing)) {
+                fireEvent(new ServerRemoved(clusterMetaDataUpdated, uri));
+            }
+
         }
 
-        clusters.put(server, cluster);
-    }
+        private HashSet<URI> locations(ClusterMetaData updated) {
+            return new HashSet<URI>(Arrays.asList(updated.getLocations()));
+        }
 
-    private static ClusterMetaData getClusterMetaData(ServerMetaData server) {
-        ClusterMetaData cluster = clusters.get(server);
-        if (cluster == null) {
-            cluster = new ClusterMetaData(0, server.getLocation());
-            clusters.put(server, cluster);
+        public Properties getProperties() {
+            return properties;
         }
 
-        return cluster;
-    }
+        public Options getOptions() {
+            return options;
+        }
 
-    private boolean getRetry() {
-        return retry = Boolean.valueOf(System.getProperty("openejb.client.requestretry", retry + ""));
+        public Set<URI> diff(Set<URI> a, Set<URI> b) {
+            final Set<URI> diffs = new HashSet<URI>();
+            for (URI uri : b) {
+                if (!a.contains(uri)) diffs.add(uri);
+            }
+
+            return diffs;
+        }
     }
 }

Modified: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ClusterMetaData.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ClusterMetaData.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ClusterMetaData.java (original)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ClusterMetaData.java Fri Mar 23 06:18:25 2012
@@ -30,7 +30,8 @@ public class ClusterMetaData implements 
     private URI[] locations;
     private long version;
     private String connectionStrategy;
-    private URI lastLocation;
+    private volatile URI lastLocation;
+    private transient final Context context = new Context();
 
     public ClusterMetaData() {
     }
@@ -40,6 +41,10 @@ public class ClusterMetaData implements 
         this.version = version;
     }
 
+    public Context getContext() {
+        return context;
+    }
+
     public URI getLastLocation() {
         return lastLocation;
     }

Modified: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ConnectionManager.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ConnectionManager.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ConnectionManager.java (original)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/ConnectionManager.java Fri Mar 23 06:18:25 2012
@@ -16,6 +16,13 @@
  */
 package org.apache.openejb.client;
 
+import org.apache.openejb.client.event.ConnectionFactoryAdded;
+import org.apache.openejb.client.event.ConnectionFactoryRemoved;
+import org.apache.openejb.client.event.ConnectionFailed;
+import org.apache.openejb.client.event.ConnectionStrategyAdded;
+import org.apache.openejb.client.event.ConnectionStrategyFailed;
+import org.apache.openejb.client.event.Log;
+
 import java.io.IOException;
 import java.net.URI;
 import java.util.Properties;
@@ -31,26 +38,31 @@ public class ConnectionManager {
     static {
         SocketConnectionFactory ejbdFactory = new SocketConnectionFactory();
 
-        factories.register("default", ejbdFactory);
-        factories.register("ejbd", ejbdFactory);
-        factories.register("ejbds", ejbdFactory);
+        registerFactory("default", ejbdFactory);
+        registerFactory("ejbd", ejbdFactory);
+        registerFactory("ejbds", ejbdFactory);
 
         HttpConnectionFactory httpFactory = new HttpConnectionFactory();
-        factories.register("http", httpFactory);
-        factories.register("https", httpFactory);
+        registerFactory("http", httpFactory);
+        registerFactory("https", httpFactory);
 
-        factories.register("multicast", new MulticastConnectionFactory());
-        factories.register("failover", new FailoverConnectionFactory());
+        registerFactory("multicast", new MulticastConnectionFactory());
+        registerFactory("failover", new FailoverConnectionFactory());
         
-        strategies.register("sticky", new StickyConnectionStrategy());
-        strategies.register("random", new RandomConnectionStrategy());
-        strategies.register("roundrobin", new RoundRobinConnectionStrategy());
-        strategies.register("round-robin", strategies.get("roundrobin"));
-        strategies.register("default", strategies.get("sticky"));
+        registerStrategy("sticky", new StickyConnectionStrategy());
+        registerStrategy("sticky+random", new StickyConnectionStrategy(new RandomConnectionStrategy()));
+        registerStrategy("sticky+round", new StickyConnectionStrategy(new RoundRobinConnectionStrategy()));
+        registerStrategy("random", new RandomConnectionStrategy());
+        registerStrategy("roundrobin", new RoundRobinConnectionStrategy());
+        registerStrategy("round-robin", strategies.get("roundrobin"));
+        registerStrategy("default", strategies.get("sticky"));
     }
 
 
     public static Connection getConnection(ClusterMetaData cluster, ServerMetaData server, Request req) throws IOException {
+        if (cluster == null) throw new IllegalArgumentException("cluster cannot be null");
+        if (server == null) throw new IllegalArgumentException("server cannot be null");
+
         String name = cluster.getConnectionStrategy();
 
         if (req instanceof EJBRequest) {
@@ -58,41 +70,73 @@ public class ConnectionManager {
             final Properties p = ejbRequest.getEjbMetaData().getProperties();
             name = p.getProperty("openejb.client.connection.strategy", name);
         }
+
         if (name == null) name = "default";
 
         ConnectionStrategy strategy = strategies.get(name);
 
-        if (strategy == null) throw new IOException("Unsupported ConnectionStrategy  \"" + name + "\"");
+        try {
+            if (strategy == null) throw new UnsupportedConnectionStrategyException(name);
 
-        logger.fine("connect: strategy=" + name + ", uri=" + server.getLocation() + ", strategy-impl=" + strategy.getClass().getName());
-        return strategy.connect(cluster, server);
+            // On finest because this happens every invocation
+            logger.finest("connect: strategy=" + name + ", uri=" + server.getLocation() + ", strategy-impl=" + strategy.getClass().getName());
+
+            return strategy.connect(cluster, server);
+        } catch (IOException e) {
+            Client.fireEvent(new ConnectionStrategyFailed(strategy, cluster, server, e));
+            throw e;
+        }
     }
 
     public static Connection getConnection(URI uri) throws IOException {
+        if (uri == null) throw new IllegalArgumentException("uri cannot be null");
         String scheme = uri.getScheme();
 
         ConnectionFactory factory = factories.get(scheme);
 
-        if (factory == null) throw new IOException("Unsupported ConnectionFactory URI scheme  \"" + scheme + "\"");
-
-        logger.fine("connect: scheme=" + scheme + ", uri=" + uri + ", factory-impl=" + factory.getClass().getName());
-        return factory.getConnection(uri);
+        try {
+            if (factory == null) {
+                throw new UnsupportedConnectionFactoryException(scheme);
+            }
+
+            // On finest because this happens every invocation
+            logger.finest("connect: scheme=" + scheme + ", uri=" + uri + ", factory-impl=" + factory.getClass().getName());
+
+            return factory.getConnection(uri);
+        } catch (IOException e) {
+            Client.fireEvent(new ConnectionFailed(uri, e));
+            throw e;
+        }
     }
 
     public static void registerFactory(String scheme, ConnectionFactory factory) {
         factories.register(scheme, factory);
+        Client.fireEvent(new ConnectionFactoryAdded(scheme, factory));
     }
 
     public static ConnectionFactory unregisterFactory(String scheme) {
-        return factories.unregister(scheme);
+        final ConnectionFactory factory = factories.unregister(scheme);
+
+        if (factory != null){
+            Client.fireEvent(new ConnectionFactoryRemoved(scheme, factory));
+        }
+
+        return factory;
     }
 
     public static void registerStrategy(String scheme, ConnectionStrategy factory) {
         strategies.register(scheme, factory);
+        Client.fireEvent(new ConnectionStrategyAdded(scheme, factory));
     }
 
     public static ConnectionStrategy unregisterStrategy(String scheme) {
-        return strategies.unregister(scheme);
+        final ConnectionStrategy strategy = strategies.unregister(scheme);
+
+        if (strategy != null) {
+            Client.fireEvent(new ConnectionStrategyAdded(scheme, strategy));
+        }
+
+        return strategy;
     }
 
     /**
@@ -103,4 +147,18 @@ public class ConnectionManager {
     public static void setFactory(ConnectionFactory factory) throws IOException {
         registerFactory("default", factory);
     }
+
+    @Log(Log.Level.SEVERE)
+    public static class UnsupportedConnectionStrategyException extends IOException {
+        public UnsupportedConnectionStrategyException(String message) {
+            super(message);
+        }
+    }
+
+    @Log(Log.Level.SEVERE)
+    public static class UnsupportedConnectionFactoryException extends IOException {
+        public UnsupportedConnectionFactoryException(String message) {
+            super(message);
+        }
+    }
 }

Added: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Context.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Context.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Context.java (added)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/Context.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,42 @@
+/*
+ * 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.client;
+
+import java.util.HashMap;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class Context {
+
+    private final HashMap<Class, Object> components = new HashMap<Class, Object>();
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T getComponent(Class<T> type) {
+        return (T) components.get(type);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T setComponent(Class<T> type, T component) {
+        return (T) components.put(type, component);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T removeComponent(Class<T> type) {
+        return (T) components.remove(type);
+    }
+}

Modified: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java (original)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EJBRequest.java Fri Mar 23 06:18:25 2012
@@ -483,14 +483,24 @@ public class EJBRequest implements Clust
         return serverHash;
     }
 
+    @Override
     public String toString() {
-        StringBuilder s = new StringBuilder((requestMethod == null ? "null" : requestMethod.toString()));
-        s.append(':').append(deploymentId);
+        final StringBuilder sb = new StringBuilder();
+        sb.append("EJBRequest{");
+        sb.append("deploymentId='");
+        sb.append(deploymentId);
+        sb.append("'");
+
+        if (requestMethod != null) {
+            sb.append(", type=").append(requestMethod);
+        }
         if (body != null) {
-            s.append(':').append(body.getMethodName());
-            s.append(':').append(body.getPrimaryKey());
+            sb.append(", method='").append(body.getMethodName());
+            sb.append("', primaryKey='").append(body.getPrimaryKey());
+            sb.append("'");
         }
-        return s.toString();
+        sb.append("}");
+        return sb.toString();
     }
 
     /*

Added: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EventLogger.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EventLogger.java?rev=1304198&view=auto
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EventLogger.java (added)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/EventLogger.java Fri Mar 23 06:18:25 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.client;
+
+import org.apache.openejb.client.event.ClusterMetaDataUpdated;
+import org.apache.openejb.client.event.Log;
+import org.apache.openejb.client.event.Observes;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class EventLogger {
+
+    public void log(@Observes ClusterMetaDataUpdated event) {
+        final Logger logger = Logger.getLogger(event.getClass().getName());
+
+        final ClusterMetaData cluster = event.getClusterMetaData();
+
+        final String msg = event.toString();
+
+        if (logger.isLoggable(Level.FINE)) {
+            logger.log(Level.FINE, msg);
+        }
+
+        if (logger.isLoggable(Level.FINER)) {
+            int i = 0;
+            for (URI uri : cluster.getLocations()) {
+                final String format = String.format("%s #%s %s", msg, ++i, uri.toASCIIString());
+                logger.log(Level.FINER, format);
+            }
+        }
+    }
+
+    public void log(@Observes Object event) {
+        final Log log = event.getClass().getAnnotation(Log.class);
+
+        if (log == null) return;
+
+        final Logger logger = Logger.getLogger(event.getClass().getName());
+
+        try {
+            final Level level = Level.parse(log.value().name());
+
+            if (logger.isLoggable(level)) {
+                logger.log(level, event.toString());
+            }
+
+        } catch (IllegalArgumentException e) {
+            logger.log(Level.WARNING, event.toString());
+        }
+    }
+}

Modified: openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java?rev=1304198&r1=1304197&r2=1304198&view=diff
==============================================================================
--- openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java (original)
+++ openejb/trunk/openejb/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java Fri Mar 23 06:18:25 2012
@@ -16,6 +16,7 @@
  */
 package org.apache.openejb.client;
 
+import org.apache.openejb.client.event.RemoteInitialContextCreated;
 import org.omg.CORBA.ORB;
 
 import javax.naming.AuthenticationException;
@@ -102,7 +103,7 @@ public class JNDIContext implements Init
         String providerUrl = (String) env.get(Context.PROVIDER_URL);
         moduleId = (String) env.get("openejb.client.moduleId");
 
-        URI location;
+        final URI location;
         try {
             providerUrl = addMissingParts(providerUrl);
             location = new URI(providerUrl);
@@ -110,6 +111,15 @@ public class JNDIContext implements Init
             throw (ConfigurationException) new ConfigurationException("Property value for " + Context.PROVIDER_URL + " invalid: " + providerUrl + " - " + e.getMessage()).initCause(e);
         }
         this.server = new ServerMetaData(location);
+
+        final Client.Context context = Client.getContext(this.server);
+        context.getProperties().putAll(environment);
+
+        final String strategy = context.getOptions().get("openejb.client.connection.strategy", "default");
+        context.getClusterMetaData().setConnectionStrategy(strategy);
+
+        Client.fireEvent(new RemoteInitialContextCreated(location));
+
         //TODO:1: Either aggressively initiate authentication or wait for the
         //        server to send us an authentication challange.
         if (userID != null) {