[jboss-svn-commits] JBoss Common SVN: r4190 - invokablecontainer/trunk/api/src/main/java/org/jboss/invokable.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Mar 25 11:05:25 EDT 2010


Author: david.lloyd at jboss.com
Date: 2010-03-25 11:05:24 -0400 (Thu, 25 Mar 2010)
New Revision: 4190

Added:
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ClientInvocationContext.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/CopyOnWriteHashMap.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocation.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocationScheme.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatchingInvocationProcessor.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ExportedInvocationDispatcher.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FieldSetter.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationForwarder.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationReply.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoClientContextException.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoNodeContextException.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoServerContextException.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociation.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociationHandle.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeInvocationContext.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeLocation.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/RemoteInvokerContext.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ServerInvocationContext.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Visitor.java
Modified:
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FastCopyHashMap.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Invocation.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationDispatcher.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationProcessor.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Keys.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ObjectInvocationDispatcher.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProcessingInvocationDispatcher.java
   invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProxyInvocationHandler.java
Log:
Commit this before I break it again...!

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ClientInvocationContext.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ClientInvocationContext.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ClientInvocationContext.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.util.Map;
+
+/**
+ * A client invocation context is an invocation environment which supports outbound method invocations only.
+ *
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public class ClientInvocationContext {
+    static {
+        // configure the global context (todo)
+        globalContext = null;
+    }
+
+    ClientInvocationContext() {
+    }
+
+    private static final ClientInvocationContext globalContext;
+
+    private static final ThreadLocal<ClientInvocationContext> current = new ThreadLocal<ClientInvocationContext>();
+
+    public static ClientInvocationContext getCurrent() {
+        final ClientInvocationContext context = current.get();
+        return context == null ? globalContext : context;
+    }
+
+    public static ClientInvocationContext requireCurrent() throws NoClientContextException {
+        final ClientInvocationContext context = getCurrent();
+        if (context == null) {
+            throw new NoClientContextException();
+        }
+        return context;
+    }
+
+    private final Map<String, InvocationForwarder> forwarders = new CopyOnWriteHashMap<String, InvocationForwarder>();
+
+    protected InvocationForwarder getForwarder(String remoteNodeName, String remoteContextName) {
+        return forwarders.get(remoteContextName);
+    }
+
+    public void start() {
+        if (current.get() != null) {
+            throw new IllegalStateException("A client invocation context is already associated with the current thread");
+        }
+        current.set(this);
+    }
+
+    public void end() {
+        if (current.get() == this) {
+            current.set(null);
+        }
+    }
+
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/CopyOnWriteHashMap.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/CopyOnWriteHashMap.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/CopyOnWriteHashMap.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,197 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ * @param <K> key type
+ * @param <V> value type
+ */
+final class CopyOnWriteHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
+    private volatile Map<K, V> map = Collections.emptyMap();
+
+    public V putIfAbsent(final K key, final V value) {
+        if (key == null) {
+            throw new IllegalArgumentException("key is null");
+        }
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            if (map.containsKey(key)) {
+                return map.get(key);
+            }
+            this.map = new FastCopyHashMap<K, V>(map, key, value);
+            return null;
+        }
+    }
+
+    public boolean remove(final Object key, final Object value) {
+        if (key == null) return false;
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            final V mapVal = map.get(key);
+            if (value == null && mapVal == null || value != null && value.equals(mapVal)) {
+                if (map.size() == 1) {
+                    this.map = Collections.emptyMap();
+                } else {
+                    final FastCopyHashMap<K, V> newMap = new FastCopyHashMap<K, V>(map);
+                    newMap.remove(key);
+                    this.map = newMap;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean replace(final K key, final V oldValue, final V newValue) {
+        if (key == null) {
+            throw new IllegalArgumentException("key is null");
+        }
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            final V mapVal = map.get(key);
+            if (oldValue == null && mapVal == null || oldValue != null && oldValue.equals(mapVal)) {
+                this.map = new FastCopyHashMap<K, V>(map, key, newValue);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public V replace(final K key, final V value) {
+        if (key == null) {
+            throw new IllegalArgumentException("key is null");
+        }
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            if (map.containsKey(key)) {
+                try {
+                    return map.get(key);
+                } finally {
+                    this.map = new FastCopyHashMap<K, V>(map, key, value);
+                }
+            }
+        }
+        return null;
+    }
+
+    public Set<Entry<K, V>> entrySet() {
+        return Collections.unmodifiableSet(map.entrySet());
+    }
+
+    public V get(final Object key) {
+        return map.get(key);
+    }
+
+    public int size() {
+        return map.size();
+    }
+
+    public boolean containsKey(final Object key) {
+        return map.containsKey(key);
+    }
+
+    public boolean containsValue(final Object value) {
+        return map.containsValue(value);
+    }
+
+    public void clear() {
+        map = Collections.emptyMap();
+    }
+
+    public Set<K> keySet() {
+        return Collections.unmodifiableSet(map.keySet());
+    }
+
+    public Collection<V> values() {
+        return Collections.unmodifiableCollection(map.values());
+    }
+
+    public V put(final K key, final V value) {
+        if (key == null) {
+            throw new IllegalArgumentException("key is null");
+        }
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            try {
+                return map.get(key);
+            } finally {
+                this.map = new FastCopyHashMap<K,V>(map, key, value);
+            }
+        }
+    }
+
+    public V remove(final Object key) {
+        if (key == null) return null;
+        synchronized (this) {
+            final Map<K, V> map = this.map;
+            if (map.containsKey(key)) {
+                if (map.size() == 1) {
+                    try {
+                        return map.get(key);
+                    } finally {
+                        this.map = Collections.emptyMap();
+                    }
+                } else {
+                    final FastCopyHashMap<K, V> newMap = new FastCopyHashMap<K, V>(map);
+                    try {
+                        return newMap.remove(key);
+                    } finally {
+                        this.map = newMap;
+                    }
+                }
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public void putAll(final Map<? extends K, ? extends V> m) {
+        synchronized (this) {
+            final FastCopyHashMap<K, V> newMap = new FastCopyHashMap<K, V>(map);
+            newMap.putAll(m);
+            map = newMap;
+        }
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    protected CopyOnWriteHashMap<K, V> clone() {
+        try {
+            final CopyOnWriteHashMap<K, V> clone = (CopyOnWriteHashMap<K, V>) super.clone();
+            final Map<K, V> map = this.map;
+            if (! map.isEmpty()) {
+                clone.map = new FastCopyHashMap<K, V>(map);
+            }
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException();
+        }
+    }
+}
\ No newline at end of file

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocation.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocation.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocation.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,95 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+public final class DispatcherLocation implements Serializable {
+
+    private static final long serialVersionUID = -8104174497055191121L;
+
+    private static final FieldSetter<DispatcherLocation> hashCodeSetter = FieldSetter.forField(DispatcherLocation.class, "hashCode");
+
+    private final NodeLocation nodeLocation;
+    private final String contextName;
+    private final String dispatcherName;
+    private transient final int hashCode;
+
+    public DispatcherLocation(final NodeLocation nodeLocation, final String contextName, final String dispatcherName) {
+        if (nodeLocation == null) {
+            throw new IllegalArgumentException("nodeLocation is null");
+        }
+        if (contextName == null) {
+            throw new IllegalArgumentException("contextName is null");
+        }
+        if (dispatcherName == null) {
+            throw new IllegalArgumentException("dispatcherName is null");
+        }
+        this.nodeLocation = nodeLocation;
+        this.contextName = contextName;
+        this.dispatcherName = dispatcherName;
+        hashCode = hashCode(nodeLocation, contextName, dispatcherName);
+    }
+
+    private static int hashCode(final NodeLocation nodeLocation, final String contextName, final String dispatcherName) {
+        return (nodeLocation.hashCode() * 31 + contextName.hashCode()) * 31 + dispatcherName.hashCode();
+    }
+
+    public NodeLocation getNodeLocation() {
+        return nodeLocation;
+    }
+
+    public String getContextName() {
+        return contextName;
+    }
+
+    public String getDispatcherName() {
+        return dispatcherName;
+    }
+
+    public boolean equals(final Object obj) {
+        return obj instanceof DispatcherLocation && equals((DispatcherLocation) obj);
+    }
+
+    public boolean equals(final DispatcherLocation other) {
+        return this == other || other != null
+                && nodeLocation.equals(other.nodeLocation)
+                && contextName.equals(other.contextName)
+                && dispatcherName.equals(other.dispatcherName);
+    }
+
+    public int hashCode() {
+        return hashCode;
+    }
+
+    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        ois.defaultReadObject();
+        hashCodeSetter.setInt(this, hashCode(nodeLocation, contextName, dispatcherName));
+    }
+
+    public String toString() {
+        return String.format("Dispatcher location {nodeLocation=\"%s\", contextName=\"%s\", dispatcherName=\"%s\"}", nodeLocation, contextName, dispatcherName);
+    }
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocationScheme.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocationScheme.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatcherLocationScheme.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+/**
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public interface DispatcherLocationScheme {
+
+    /**
+     * Get a dispatcher.  Returns {@code null} if no dispatcher matches the given specification.
+     *
+     * @param locationName the location name
+     * @param remoteContextName the remote context name
+     * @param dispatcherName the dispatcher name
+     * @return the dispatcher
+     */
+    InvocationDispatcher getInvocationDispatcher(String locationName, String remoteContextName, String dispatcherName);
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatchingInvocationProcessor.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatchingInvocationProcessor.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/DispatchingInvocationProcessor.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+class DispatchingInvocationProcessor implements InvocationProcessor, Serializable {
+
+    private static final long serialVersionUID = 2537460849611664946L;
+
+    DispatchingInvocationProcessor() {
+    }
+
+    public InvocationReply processInvocation(final InvocationDispatcher dispatcher, final Invocation invocation) throws InvocationException {
+        return dispatcher.dispatch(invocation);
+    }
+
+    public <I> void accept(final Visitor<InvocationProcessor, I> visitor, final I param) {
+        visitor.visit(this, param);
+    }
+
+    protected Object readResolve() {
+        return InvocationProcessor.DISPATCHING;
+    }
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ExportedInvocationDispatcher.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ExportedInvocationDispatcher.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ExportedInvocationDispatcher.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+final class ExportedInvocationDispatcher implements InvocationDispatcher, Serializable {
+
+    private static final long serialVersionUID = -3950038476252367014L;
+
+    private transient final NodeInvocationContext nodeInvocationContext;
+    private final DispatcherLocation dispatcherLocation;
+
+    private static final FieldSetter<ExportedInvocationDispatcher> nodeInvocationContextSetter = FieldSetter.forField(ExportedInvocationDispatcher.class, "nodeInvocationContext");
+
+    ExportedInvocationDispatcher(final NodeInvocationContext nodeInvocationContext, final DispatcherLocation dispatcherLocation) {
+        this.nodeInvocationContext = nodeInvocationContext;
+        this.dispatcherLocation = dispatcherLocation;
+    }
+
+    public InvocationReply dispatch(final Invocation invocation) throws InvocationException {
+        final NodeAssociation association = nodeInvocationContext.getNodeAssociation(dispatcherLocation.getNodeLocation());
+        final InvocationForwarder forwarder = association.getInvocationForwarder(dispatcherLocation.getContextName());
+        try {
+            return forwarder.dispatch(dispatcherLocation.getDispatcherName(), invocation);
+        } catch (IOException e) {
+            throw new IOError(e);
+        }
+    }
+
+    public <I> void accept(final Visitor<InvocationDispatcher, I> visitor, final I param) {
+        visitor.visit(this, param);
+    }
+
+
+    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        ois.defaultReadObject();
+        nodeInvocationContextSetter.set(this, NodeInvocationContext.requireCurrent());
+    }
+}

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FastCopyHashMap.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FastCopyHashMap.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FastCopyHashMap.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -47,7 +47,7 @@
  *
  * @author Jason T. Greene
  */
-class FastCopyHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable
+final class FastCopyHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable
 {
    /**
     * Marks null keys.
@@ -138,6 +138,12 @@
       }
    }
 
+   public FastCopyHashMap(final Map<K, V> map, final K key, final V value)
+   {
+      this(map);
+      put(key, value);
+   }
+
    @SuppressWarnings("unchecked")
    private void init(int initialCapacity, float loadFactor)
    {

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FieldSetter.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FieldSetter.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/FieldSetter.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+final class FieldSetter<T> {
+    private final Field field;
+
+    private FieldSetter(final Field field) {
+        this.field = field;
+    }
+
+    static <T> FieldSetter<T> forField(final Class<T> clazz, final String name) {
+        return AccessController.doPrivileged(new PrivilegedAction<FieldSetter<T>>() {
+            public FieldSetter<T> run() {
+                try {
+                    final Field field = clazz.getDeclaredField(name);
+                    field.setAccessible(true);
+                    return new FieldSetter<T>(field);
+                } catch (NoSuchFieldException e) {
+                    throw new NoSuchFieldError(e.getMessage());
+                }
+            }
+        });
+    }
+
+    public void set(final T obj, final Object value) {
+        try {
+            field.set(obj, value);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setBoolean(final T obj, final boolean z) {
+        try {
+            field.setBoolean(obj, z);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setByte(final T obj, final byte b) {
+        try {
+            field.setByte(obj, b);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setChar(final T obj, final char c) {
+        try {
+            field.setChar(obj, c);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setShort(final T obj, final short s) {
+        try {
+            field.setShort(obj, s);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setInt(final T obj, final int i) {
+        try {
+            field.setInt(obj, i);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setLong(final T obj, final long l) {
+        try {
+            field.setLong(obj, l);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setFloat(final T obj, final float f) {
+        try {
+            field.setFloat(obj, f);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public void setDouble(final T obj, final double d) {
+        try {
+            field.setDouble(obj, d);
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+}

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Invocation.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Invocation.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Invocation.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -25,13 +25,8 @@
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.io.ObjectStreamField;
 import java.io.Serializable;
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Arrays;
 
 /**
  * A unified view of a {@link Method} invocation.  Composes the target method, arguments, and mapped context into a single
@@ -44,46 +39,30 @@
 public final class Invocation implements Serializable {
 
     private static final long serialVersionUID = 2691631710015102611L;
-    private static final Field targetMethodField;
 
     /**
-     * Serialization of an {@code Invocation} is handled specially.  The {@code args} and {@code context} fields are
-     * serialized normally, but because {@link Method} is not serializable, the {@code targetMethod} field is not directly
-     * serialized.  Instead, it is written (after the two aforementioned fields) as three distinct objects:
-     * <ol>
-     * <li><b>Declaring Class</b> - this is the result of {@code targetMethod.getDeclaringClass()}.</li>
-     * <li><b>Method Name</b> - this is the string name, as returned by {@code targetMethod.getName()}.</li>
-     * <li><b>Parameter Types</b> - this is an array of {@code Class} objects which represents the method's parameter
-     * types, as returned by {@code targetMethod.getParameterTypes()}.
-     * </ol>
-     * On deserialization, the method is then looked up on the declaring class.  If no valid, matching public method
-     * is found, an {@link java.io.InvalidObjectException} is thrown and deserialization fails.
+     * This field contains the method-call arguments.
      */
-    private static final ObjectStreamField[] serialPersistentFields = {
-            new ObjectStreamField("args", Object[].class),
-            new ObjectStreamField("context", InvocationProperties.class),
-    };
+    private transient final Object[] args;
 
     /**
-     * This field contains the method-call arguments.
-     *
-     * @serial
+     * This field specifies the target method.
      */
-    private final Object[] args;
-
-    private static final Object[] NONE = new Object[0];
-
-    @SuppressWarnings({ "InstanceVariableMayNotBeInitializedByReadObject" })
     private transient final Method targetMethod;
 
     /**
      * This field contains the invocation context.  As a special case, if the context is empty at serialization
      * time, it is written as {@code null} to conserve bandwidth.
-     *
-     * @serial
      */
-    private volatile InvocationProperties properties;
+    private transient volatile InvocationProperties properties;
 
+    private static final Object[] NO_OBJECTS = new Object[0];
+    private static final Class<?>[] NO_CLASSES = new Class[0];
+
+    private static final FieldSetter<Invocation> argsSetter = FieldSetter.forField(Invocation.class, "args");
+    private static final FieldSetter<Invocation> targetMethodSetter = FieldSetter.forField(Invocation.class, "targetMethod");
+    private static final FieldSetter<Invocation> propertiesSetter = FieldSetter.forField(Invocation.class, "properties");
+
     /**
      * Construct a new instance.
      *
@@ -96,7 +75,7 @@
             throw new IllegalArgumentException("targetMethod is null");
         }
         this.targetMethod = targetMethod;
-        this.args = defaulted(args, NONE);
+        this.args = defaulted(args, NO_OBJECTS);
         this.properties = defaulted(properties, InvocationProperties.EMPTY);
     }
 
@@ -116,23 +95,9 @@
      * @param targetMethod the method being invoked
      */
     public Invocation(final Method targetMethod) {
-        this(InvocationProperties.EMPTY, targetMethod, NONE);
+        this(InvocationProperties.EMPTY, targetMethod, NO_OBJECTS);
     }
 
-    static {
-        targetMethodField = AccessController.doPrivileged(new PrivilegedAction<Field>() {
-            public Field run() {
-                try {
-                    final Field field = Invocation.class.getDeclaredField("targetMethod");
-                    field.setAccessible(true);
-                    return field;
-                } catch (NoSuchFieldException e) {
-                    throw new IllegalStateException(e);
-                }
-            }
-        });
-    }
-
     //-------------------------------------------------------------------------------------||
     // Contracts --------------------------------------------------------------------------||
     //-------------------------------------------------------------------------------------||
@@ -178,86 +143,42 @@
     // Serialization ----------------------------------------------------------------------||
     //-------------------------------------------------------------------------------------||
 
-    /**
-     * Serializes the invocation with a custom form
-     *
-     * @serialData After all non-transient fields are written, we pipe out the target method's declaring class, method
-     * name, and array of parameter types.  These are used to reconstitute the target Method, which is not itself
-     * Serializable
-     */
-    private void writeObject(final ObjectOutputStream out) throws IOException {
-        // Default write of non-transient fields
-        out.defaultWriteObject();
-        // Write out the target method by decomposing it into Serializable parts
+    private void writeObject(ObjectOutputStream oos) throws IOException {
+        oos.defaultWriteObject();
         final Method targetMethod = this.targetMethod;
-        final Class<?> declaringClass = targetMethod.getDeclaringClass();
-        final String name = targetMethod.getName();
-        final Class<?>[] paramTypes = targetMethod.getParameterTypes();
-        out.writeObject(declaringClass);
-        out.writeObject(name);
-        out.writeObject(paramTypes);
-        // See if the invocation context is empty
+        oos.writeObject(targetMethod.getDeclaringClass());
+        oos.writeUTF(targetMethod.getName());
+        final Class<?>[] parameterTypes = targetMethod.getParameterTypes();
+        oos.writeObject(parameterTypes.length == 0 ? null : parameterTypes);
+        final Object[] args = this.args;
+        oos.writeObject(args.length == 0 ? null : args);
         final InvocationProperties properties = this.properties;
-        if (properties.isEmpty()) {
-            // Null out; we don't need to send empty properties.  On deserialization
-            // we'll just re-instantiate.
-            out.writeObject(null);
-        } else {
-            // Write invocation context
-            out.writeObject(properties);
-        }
+        oos.writeObject(properties.isEmpty() ? null : properties);
     }
 
-    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
-        // Get default form
-        in.defaultReadObject();
-        // Assert invariants
-        if (getArgs() == null) {
-            throw new IllegalArgumentException("Deserialization resulted in null arguments to the target method");
-        }
-        // Reconstruct the Method by getting the declaring class, method name, and signature
-        final Class<?> declaringClass = (Class<?>) in.readObject();
-        final String methodName = (String) in.readObject();
-        final Class<?>[] paramTypes = (Class<?>[]) in.readObject();
-        // Some more assertions
-        if (declaringClass == null) {
-            throw new InvalidObjectException("Declaring class was not read in during deserialization");
-        }
-        if (methodName == null) {
-            throw new InvalidObjectException("Target method name was not read in during deserialization");
-        }
-        if (paramTypes == null) {
-            throw new InvalidObjectException(
-                    "Parameter types of the target method were not read in during deserialization");
-        }
-        // Obtain the target method
+    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        ois.defaultReadObject();
+        final Class<?> declaringClass = nonNull((Class<?>) ois.readObject());
+        final String name = ois.readUTF();
+        final Class<?>[] parameterTypes = defaulted((Class<?>[]) ois.readObject(), NO_CLASSES);
         final Method targetMethod;
         try {
-            targetMethod = declaringClass.getMethod(methodName, paramTypes);
-        } catch (final NoSuchMethodException e) {
-            throw new InvalidObjectException("Could not deserialize; no target method \"" + methodName + "\" found in "
-                    + declaringClass.getName() + " with param types " + Arrays.asList(paramTypes));
+            targetMethod = declaringClass.getMethod(name, parameterTypes);
+        } catch (NoSuchMethodException e) {
+            throw new InvalidObjectException("Invocation references a missing method");
         }
-        // Set the target method
-        setTargetMethod(targetMethod);
-        // Set the InvocationContext
-        final InvocationProperties properties = (InvocationProperties) in.readObject();
-        // If no context is provided
-        if (properties == null) {
-            // Replace with a new instance; this signals empty properties were present when the instance
-            // was serialized
-            this.properties = InvocationProperties.EMPTY;
-        } else {
-            this.properties = properties;
-        }
+        targetMethodSetter.set(this, targetMethod);
+        final Object[] args = defaulted((Object[]) ois.readObject(), NO_OBJECTS);
+        argsSetter.set(this, args);
+        final InvocationProperties properties = defaulted((InvocationProperties) ois.readObject(), InvocationProperties.EMPTY);
+        propertiesSetter.set(this, properties);
     }
 
-    private void setTargetMethod(Method method) {
-        try {
-            targetMethodField.set(this, method);
-        } catch (IllegalAccessException e) {
-            throw new IllegalStateException(e);
+    private static <T> T nonNull(T value) throws InvalidObjectException {
+        if (value == null) {
+            throw new InvalidObjectException("unexpected null value");
         }
+        return value;
     }
 
     private static <T> T defaulted(T value, T defaultValue) {

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationDispatcher.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationDispatcher.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationDispatcher.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -42,5 +42,14 @@
      * obtained via {@link InvocationException#getCause()}
      * @throws IllegalArgumentException If the invocation is not specified (i.e. {@code null})
      */
-    Object dispatch(Invocation invocation) throws InvocationException;
+    InvocationReply dispatch(Invocation invocation) throws InvocationException;
+
+    /**
+     * Pass this dispatcher to a visitor, followed by any delegate dispatcher.
+     *
+     * @param visitor the visitor to visit
+     * @param param the parameter
+     * @param <I> the parameter type, possibly {@link Void}
+     */
+    <I> void accept(Visitor<InvocationDispatcher, I> visitor, I param);
 }

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationForwarder.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationForwarder.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationForwarder.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOException;
+
+/**
+ * Forwards an invocation to a remote Server Invocation Context, possibly across some network connection.  While an
+ * {@code InvocationDispatcher} represents a single invocation target, instances of this interface represent a single
+ * network path to a peer, which may have many {@code InvocationDispatcher}s on the receiving side.  An instance of
+ * this interface is associated with one remote Server Invocation Context or a cluster of Server Invocation Contexts.
+ *
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public interface InvocationForwarder {
+
+    /**
+     * Send an invocation across the network.
+     *
+     * @param dispatcherName the identifier of the dispatcher to use on the remote side
+     * @param invocation the invocation to execute
+     * @return the result of the invocation
+     * @throws IOException if an I/O error occurs
+     * @throws InvocationException if a remote invocation failed
+     */
+    InvocationReply dispatch(String dispatcherName, Invocation invocation) throws IOException, InvocationException;
+}

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationProcessor.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationProcessor.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationProcessor.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -40,14 +40,19 @@
      * obtained via {@link InvocationException#getCause()}
      * @throws IllegalArgumentException If the invocation or dispatcher is not specified (i.e. {@code null})
      */
-    Object processInvocation(InvocationDispatcher dispatcher, Invocation invocation) throws InvocationException, IllegalArgumentException;
+    InvocationReply processInvocation(InvocationDispatcher dispatcher, Invocation invocation) throws InvocationException, IllegalArgumentException;
 
     /**
-     * A simple dispatching invocation processor, used typically at the end of an interceptor chain.
+     * Pass this processor to a visitor, followed by any delegate processor.
+     *
+     * @param visitor the visitor to visit
+     * @param param the parameter
+     * @param <I> the parameter type, possibly {@link Void}
      */
-    InvocationProcessor DISPATCHING = new InvocationProcessor() {
-        public Object processInvocation(final InvocationDispatcher dispatcher, final Invocation invocation) throws InvocationException {
-            return dispatcher.dispatch(invocation);
-        }
-    };
+    <I> void accept(Visitor<InvocationProcessor, I> visitor, I param);
+
+    /**
+     * A simple (serializable) dispatching invocation processor, used typically at the end of an interceptor chain.
+     */
+    InvocationProcessor DISPATCHING = new DispatchingInvocationProcessor();
 }

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationReply.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationReply.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/InvocationReply.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+public final class InvocationReply implements Serializable {
+
+    private static final long serialVersionUID = 4330152364952496586L;
+
+    private static final FieldSetter<InvocationReply> replySetter = FieldSetter.forField(InvocationReply.class, "reply");
+
+    private transient final Object reply;
+    private transient volatile InvocationProperties properties;
+
+    public InvocationReply(final Object reply) {
+        this.reply = reply;
+    }
+
+    public InvocationReply(final Object reply, final InvocationProperties properties) {
+        this.reply = reply;
+        this.properties = properties;
+    }
+
+    public Object getReply() {
+        return reply;
+    }
+
+    public InvocationProperties getProperties() {
+        return properties;
+    }
+
+    public void setProperties(final InvocationProperties properties) {
+        this.properties = properties;
+    }
+
+    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        ois.defaultReadObject();
+        replySetter.set(this, ois.readObject());
+        properties = defaulted((InvocationProperties) ois.readObject(), InvocationProperties.EMPTY);
+    }
+
+    private static <T> T defaulted(T value, T defaultValue) {
+        return value == null ? defaultValue : value;
+    }
+}

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Keys.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Keys.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Keys.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -38,4 +38,8 @@
      * The transaction associated with this invocation.  Should yield a {@link javax.transaction.Transaction} (?).
      */
     TRANSACTION,
+    /**
+     * The session identifier, for distinguishing between more than one target instance for a given dispatcher.
+     */
+    SESSION,
 }

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoClientContextException.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoClientContextException.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoClientContextException.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+public final class NoClientContextException extends IllegalStateException {
+
+    private static final long serialVersionUID = -1739332805464223362L;
+
+    /**
+     * Constructs a {@code NoClientContextException} with no detail message. The cause is not initialized, and may
+     * subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     */
+    public NoClientContextException() {
+    }
+
+    /**
+     * Constructs a {@code NoClientContextException} with the specified detail message. The cause is not initialized, and
+     * may subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     *
+     * @param msg the detail message
+     */
+    public NoClientContextException(final String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a {@code NoClientContextException} with the specified cause. The detail message is set to:
+     * <pre>(cause == null ? null : cause.toString())</pre>
+     * (which typically contains the class and detail message of {@code cause}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoClientContextException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a {@code NoClientContextException} with the specified detail message and cause.
+     *
+     * @param msg the detail message
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoClientContextException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoNodeContextException.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoNodeContextException.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoNodeContextException.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+public final class NoNodeContextException extends IllegalStateException {
+
+    private static final long serialVersionUID = -4218112918054622891L;
+
+    /**
+     * Constructs a {@code NoNodeContextException} with no detail message. The cause is not initialized, and may
+     * subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     */
+    public NoNodeContextException() {
+    }
+
+    /**
+     * Constructs a {@code NoNodeContextException} with the specified detail message. The cause is not initialized, and may
+     * subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     *
+     * @param msg the detail message
+     */
+    public NoNodeContextException(final String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a {@code NoNodeContextException} with the specified cause. The detail message is set to:
+     * <pre>(cause == null ? null : cause.toString())</pre>
+     * (which typically contains the class and detail message of {@code cause}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoNodeContextException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a {@code NoNodeContextException} with the specified detail message and cause.
+     *
+     * @param msg the detail message
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoNodeContextException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}
\ No newline at end of file

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoServerContextException.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoServerContextException.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NoServerContextException.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+public final class NoServerContextException extends IllegalStateException {
+
+    private static final long serialVersionUID = 3873017282274298512L;
+
+    /**
+     * Constructs a {@code NoServerContextException} with no detail message. The cause is not initialized, and may
+     * subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     */
+    public NoServerContextException() {
+    }
+
+    /**
+     * Constructs a {@code NoServerContextException} with the specified detail message. The cause is not initialized, and
+     * may subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
+     *
+     * @param msg the detail message
+     */
+    public NoServerContextException(final String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a {@code NoServerContextException} with the specified cause. The detail message is set to:
+     * <pre>(cause == null ? null : cause.toString())</pre>
+     * (which typically contains the class and detail message of {@code cause}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoServerContextException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a {@code NoServerContextException} with the specified detail message and cause.
+     *
+     * @param msg the detail message
+     * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
+     */
+    public NoServerContextException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociation.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociation.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociation.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+/**
+ * An association with a node.  This should be an active or "live" association.
+ *
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public interface NodeAssociation {
+
+    /**
+     * Get an invocation forwarder for the associated remote node.
+     *
+     * @param contextName the context name
+     * @return the dispatcher, or {@code null} if none matched
+     */
+    InvocationForwarder getInvocationForwarder(String contextName);
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociationHandle.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociationHandle.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeAssociationHandle.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.Closeable;
+
+/**
+ * A handle for a node association, used to indicate to the client invocation context that the node association has
+ * ended.
+ *
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public interface NodeAssociationHandle extends Closeable {
+
+    /**
+     * Call when the node association has been closed.
+     */
+    void close();
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeInvocationContext.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeInvocationContext.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeInvocationContext.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public final class NodeInvocationContext {
+    private final ConcurrentMap<NodeLocation, NodeAssociation> nodeAssociations = new ConcurrentHashMap<NodeLocation, NodeAssociation>();
+
+    private static final ThreadLocal<NodeInvocationContext> current = new ThreadLocal<NodeInvocationContext>();
+
+    private static final NodeInvocationContext global = null; // (todo - configure)
+
+    public NodeAssociationHandle addNodeAssociation(final NodeLocation location, final NodeAssociation association) throws IllegalArgumentException {
+        if (location == null) {
+            throw new IllegalArgumentException("location is null");
+        }
+        if (association == null) {
+            throw new IllegalArgumentException("association is null");
+        }
+        final String transport = location.getTransport();
+        if (transport.length() == 0 || transport.equals("*")) {
+            throw new IllegalArgumentException(location.toString() + " does not specify a specific transport");
+        }
+        final NodeAssociation appearing = nodeAssociations.putIfAbsent(location, association);
+        if (appearing != null) {
+            throw new IllegalArgumentException(location.toString() + " is already registered");
+        }
+        return new NodeAssociationHandle() {
+            public void close() {
+                nodeAssociations.remove(location, association);
+            }
+        };
+    }
+
+    public InvocationDispatcher createDispatcher(final DispatcherLocation dispatcherLocation) {
+        return new ExportedInvocationDispatcher(this, dispatcherLocation);
+    }
+
+    public static NodeInvocationContext getCurrent() {
+        return defaulted(current.get(), global);
+    }
+
+    public static NodeInvocationContext requireCurrent() {
+        final NodeInvocationContext context = getCurrent();
+        if (context == null) {
+            throw new NoNodeContextException();
+        }
+        return context;
+    }
+
+    public void start() {
+        if (current.get() != null) {
+            throw new IllegalStateException("A node invocation context is already associated with the current thread");
+        }
+        current.set(this);
+    }
+
+    public void end() {
+        if (current.get() == this) {
+            current.set(null);
+        }
+    }
+
+    private static <T> T defaulted(T val, T defVal) {
+        return val == null ? defVal : val;
+    }
+
+    public NodeAssociation getNodeAssociation(final NodeLocation nodeLocation) {
+        // (todo - access check)
+        final String transport = nodeLocation.getTransport();
+        if (transport.length() == 0 || transport.equals("*")) {
+            // (todo - index by transport type)
+            for (Map.Entry<NodeLocation, NodeAssociation> entry : nodeAssociations.entrySet()) {
+                if (nodeLocation.isSameNode(entry.getKey())) {
+                    return entry.getValue();
+                }
+            }
+            return null;
+        } else {
+            return nodeAssociations.get(nodeLocation);
+        }
+    }
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeLocation.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeLocation.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/NodeLocation.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+public final class NodeLocation implements Serializable {
+
+    private static final long serialVersionUID = -4658944294566402276L;
+
+    private static final FieldSetter<NodeLocation> hashCodeSetter = FieldSetter.forField(NodeLocation.class, "hashCode");
+
+    private final String transport;
+    private final String scheme;
+    private final String name;
+    private transient final int hashCode;
+
+    public NodeLocation(final String transport, final String scheme, final String name) {
+        if (transport == null) {
+            throw new IllegalArgumentException("transport is null");
+        }
+        if (scheme == null) {
+            throw new IllegalArgumentException("scheme is null");
+        }
+        if (name == null) {
+            throw new IllegalArgumentException("name is null");
+        }
+        this.transport = transport;
+        this.scheme = scheme;
+        this.name = name;
+        hashCode = hashCode(transport, scheme, name);
+    }
+
+    private static int hashCode(final String transport, final String scheme, final String name) {
+        return (transport.hashCode() * 31 + scheme.hashCode()) * 31 + name.hashCode();
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getTransport() {
+        return transport;
+    }
+
+    public int hashCode() {
+        return hashCode;
+    }
+
+    public boolean isSameNode(final NodeLocation other) {
+        return this == other || other != null && scheme.equals(other.scheme) && name.equals(other.name);
+    }
+
+    public boolean equals(final Object obj) {
+        return obj instanceof NodeLocation && equals((NodeLocation) obj);
+    }
+
+    public boolean equals(final NodeLocation other) {
+        return this == other || other != null && hashCode == other.hashCode && scheme.equals(other.scheme) && name.equals(other.name) && transport.equals(other.transport);
+    }
+
+    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        ois.defaultReadObject();
+        if (transport == null) {
+            throw new IllegalArgumentException("transport is null");
+        }
+        if (scheme == null) {
+            throw new InvalidObjectException("scheme is null");
+        }
+        if (name == null) {
+            throw new InvalidObjectException("name is null");
+        }
+        hashCodeSetter.setInt(this, hashCode(transport, scheme, name));
+    }
+
+    public String toString() {
+        return String.format("Node location \"%s:%s\" via \"%s\"", scheme, name, transport);
+    }
+}

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ObjectInvocationDispatcher.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ObjectInvocationDispatcher.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ObjectInvocationDispatcher.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -49,9 +49,9 @@
     }
 
     /** {@inheritDoc} */
-    public Object dispatch(final Invocation invocation) throws InvocationException {
+    public InvocationReply dispatch(final Invocation invocation) throws InvocationException {
         try {
-            return invocation.getTargetMethod().invoke(target, invocation.getArgs());
+            return new InvocationReply(invocation.getTargetMethod().invoke(target, invocation.getArgs()));
         } catch (IllegalAccessException e) {
             throw new InvocationException(new IllegalAccessError(e.getMessage()));
         } catch (InvocationTargetException e) {
@@ -59,6 +59,11 @@
         }
     }
 
+    /** {@inheritDoc} */
+    public <I> void accept(final Visitor<InvocationDispatcher, I> visitor, final I param) {
+        visitor.visit(this, param);
+    }
+
     private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
         ois.defaultReadObject();
         final SecurityManager sm = System.getSecurityManager();

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProcessingInvocationDispatcher.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProcessingInvocationDispatcher.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProcessingInvocationDispatcher.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -67,10 +67,15 @@
     }
 
     /** {@inheritDoc}  This implementation passes the invocation through its processor. */
-    public Object dispatch(final Invocation invocation) throws InvocationException {
+    public InvocationReply dispatch(final Invocation invocation) throws InvocationException {
         return processor.processInvocation(dispatcher, invocation);
     }
 
+    public <I> void accept(final Visitor<InvocationDispatcher, I> visitor, final I param) {
+        visitor.visit(this, param);
+        dispatcher.accept(visitor, param);
+    }
+
     private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
         ois.defaultReadObject();
         if (dispatcher == null) {

Modified: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProxyInvocationHandler.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProxyInvocationHandler.java	2010-03-23 20:10:38 UTC (rev 4189)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ProxyInvocationHandler.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -59,7 +59,7 @@
      */
     public ProxyInvocationHandler(final InvocationProcessor processor, final InvocationDispatcher dispatcher) {
         this.dispatcher = dispatcher;
-        this.processor = processor;
+        this.processor = processor == null ? InvocationProcessor.DISPATCHING : processor;
     }
 
     /**

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/RemoteInvokerContext.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/RemoteInvokerContext.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/RemoteInvokerContext.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,102 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+/**
+ * The invocation context.
+ */
+public final class RemoteInvokerContext {
+
+
+    private final String name;
+
+    private static final ThreadLocal<RemoteInvokerContext> current = new ThreadLocal<RemoteInvokerContext>();
+
+    RemoteInvokerContext(final String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void begin() {
+        final ThreadLocal<RemoteInvokerContext> current = RemoteInvokerContext.current;
+        if (current.get() != null) {
+            throw new IllegalStateException("Another remote invocation context is already in effect");
+        }
+        current.set(this);
+    }
+
+    public void end() {
+        final ThreadLocal<RemoteInvokerContext> current = RemoteInvokerContext.current;
+        if (current.get() == this) {
+            current.set(null);
+        }
+    }
+
+    public static RemoteInvokerContext getCurrent() {
+        return current.get();
+    }
+
+    public static RemoteInvokerContext requireCurrent() throws IllegalStateException {
+        final RemoteInvokerContext context = getCurrent();
+        if (context == null) {
+            throw new IllegalStateException("No active invocation context");
+        }
+        return context;
+    }
+
+    /**
+     * Register a 
+     * @param name
+     * @param remoteDispatcher
+     */
+    public void addPeer(String name, InvocationDispatcher remoteDispatcher) {
+
+    }
+
+    /**
+     * Register a local invocation dispatcher, returning a serializable dispatcher which can be sent to remote clients or used
+     * locally.
+     *
+     * @param processor the invocation processor
+     * @param targetDispatcher the target dispatcher
+     * @return a serializable dispatcher
+     */
+    public InvocationDispatcher register(final InvocationProcessor processor, final InvocationDispatcher targetDispatcher) {
+        return null;
+    }
+
+    InvocationDispatcher getLocalDispatcher(final String dispatcherId) {
+        return null;
+    }
+
+    InvocationDispatcher requireLocalDispatcher(final String dispatcherId) {
+        final InvocationDispatcher dispatcher = getLocalDispatcher(dispatcherId);
+        if (dispatcher == null) {
+            throw new IllegalArgumentException("No dispatcher found");
+        }
+        return dispatcher;
+    }
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ServerInvocationContext.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ServerInvocationContext.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/ServerInvocationContext.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,152 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A server invocation context is an invocation environment which supports inbound and outbound invocations.
+ *
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public class ServerInvocationContext extends ClientInvocationContext {
+    private final String name;
+
+    private static final String NODE_NAME;
+
+    static {
+        // Inside the AS, this will use the AS settings.  Outside, it will get the node name in a similar way.
+        String hostName = System.getProperty("jboss.host.name");
+        String qualifiedHostName = System.getProperty("jboss.qualified.host.name");
+        if (qualifiedHostName == null) {
+            // if host name is specified, don't pick a qualified host name that isn't related to it
+            qualifiedHostName = hostName;
+            if (qualifiedHostName == null) {
+                // POSIX-like OSes including Mac should have this set
+                qualifiedHostName = System.getenv("HOSTNAME");
+            }
+            if (qualifiedHostName == null) {
+                // Certain versions of Windows
+                qualifiedHostName = System.getenv("COMPUTERNAME");
+            }
+            if (qualifiedHostName == null) {
+                try {
+                    qualifiedHostName = InetAddress.getLocalHost().getHostName();
+                } catch (UnknownHostException e) {
+                    qualifiedHostName = null;
+                }
+            }
+            if (qualifiedHostName != null && qualifiedHostName.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$|:")) {
+                // IP address is not acceptable
+                qualifiedHostName = null;
+            }
+            if (qualifiedHostName == null) {
+                // Give up
+                qualifiedHostName = "unknown-host.unknown-domain";
+            }
+            qualifiedHostName = qualifiedHostName.trim().toLowerCase();
+        }
+        if (hostName == null) {
+            // Use the host part of the qualified host name
+            final int idx = qualifiedHostName.indexOf('.');
+            hostName = idx == -1 ? qualifiedHostName : qualifiedHostName.substring(0, idx);
+        }
+        // Set up the node name
+        String nodeName = System.getProperty("jboss.node.name");
+        if (nodeName == null) {
+            nodeName = hostName;
+        }
+        NODE_NAME = nodeName;
+    }
+
+    ServerInvocationContext(final String name) {
+        this.name = name;
+    }
+
+    public static ServerInvocationContext getCurrent() {
+        final ClientInvocationContext current = ClientInvocationContext.getCurrent();
+        if (current instanceof ServerInvocationContext) {
+            return (ServerInvocationContext) current;
+        } else {
+            return null;
+        }
+    }
+
+    public static ServerInvocationContext requireCurrent() throws NoServerContextException {
+        final ServerInvocationContext context = getCurrent();
+        if (context == null) {
+            throw new NoServerContextException();
+        }
+        return context;
+    }
+
+    public static ServerInvocationContext create(final String name) {
+        return new ServerInvocationContext(name);
+    }
+
+    private final ConcurrentMap<String, Reg> exportedDispatchers = new ConcurrentHashMap<String, Reg>();
+
+    private static final class Reg {
+        private final InvocationDispatcher localDispatcher;
+        private final InvocationProcessor processor;
+
+        private Reg(final InvocationDispatcher localDispatcher, final InvocationProcessor processor) {
+            this.localDispatcher = localDispatcher;
+            this.processor = processor;
+        }
+    }
+
+    private final InvocationForwarder loopback = new InvocationForwarder() {
+        public InvocationReply dispatch(final String identifier, final Invocation invocation) throws IOException, InvocationException {
+            final Reg reg = exportedDispatchers.get(identifier);
+            if (reg != null) {
+                return reg.processor.processInvocation(reg.localDispatcher, invocation);
+            } else {
+                throw new IOException("No dispatcher found named \"" + identifier + "\"");
+            }
+        }
+    };
+
+    public InvocationDispatcher export(InvocationDispatcher dispatcher) {
+        dispatcher.accept(new Visitor<InvocationDispatcher, Void>() {
+            public void visit(final InvocationDispatcher visited, final Void parameter) {
+                if (visited instanceof ExportedInvocationDispatcher) {
+                    throw new IllegalArgumentException("Dispatcher is already exported");
+                }
+            }
+        }, null);
+        return new ExportedInvocationDispatcher(null, null, name, null, this);
+    }
+
+    protected InvocationForwarder getForwarder(final String remoteNodeName, final String remoteContextName) {
+        if (name.equals(remoteContextName) && remoteNodeName.equals(NODE_NAME)) {
+            return loopback;
+        } else {
+            return super.getForwarder(remoteNodeName, remoteContextName);
+        }
+    }
+}

Added: invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Visitor.java
===================================================================
--- invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Visitor.java	                        (rev 0)
+++ invokablecontainer/trunk/api/src/main/java/org/jboss/invokable/Visitor.java	2010-03-25 15:05:24 UTC (rev 4190)
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.invokable;
+
+/**
+ * A visitor for some type.
+ *
+ * @param <T> the type being visited
+ * @param <P> the parameter type (possibly {@link Void}
+ * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ */
+public interface Visitor<T, P> {
+
+    /**
+     * Visit a node.
+     *
+     * @param visited the visited object
+     * @param parameter the visitor parameter
+     */
+    void visit(T visited, P parameter);
+}



More information about the jboss-svn-commits mailing list