[jboss-remoting-commits] JBoss Remoting SVN: r5032 - in remoting3/trunk: jboss-remoting/src/main/java/org/jboss/remoting3/spi and 2 other directories.

jboss-remoting-commits at lists.jboss.org jboss-remoting-commits at lists.jboss.org
Tue Apr 14 13:18:15 EDT 2009


Author: david.lloyd at jboss.com
Date: 2009-04-14 13:18:15 -0400 (Tue, 14 Apr 2009)
New Revision: 5032

Added:
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientConnector.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSourceConnector.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointConnector.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ResourceType.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceLocationListener.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistrationListener.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceSpecification.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/Cancellable.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/ConnectionProvider.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/EndpointConnection.java
Removed:
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/FutureClientSource.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RemoteServiceConfiguration.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceListener.java
Modified:
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSource.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Endpoint.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointImpl.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/HandleableCloseable.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Remoting.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RequestListener.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistration.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceURI.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RemoteRequestContext.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandler.java
   remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandlerSource.java
   remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/EndpointTestCase.java
   remoting3/trunk/samples/src/test/java/org/jboss/remoting3/samples/protocol/basic/BasicTestCase.java
Log:
Endpoint API changes for working connection management, part 1

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientConnector.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientConnector.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientConnector.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+import java.net.URI;
+import org.jboss.xnio.IoFuture;
+
+/**
+ * A client connector.  Opens a connection to a URI which provides a single {@code Client} instance.  Instances of this
+ * interface may only be able to support a single URI scheme.  Depending on the implementation, the URI may be a
+ * protocol URI or a service URI.
+ */
+public interface ClientConnector extends HandleableCloseable<ClientConnector> {
+
+    /**
+     * Establish a client connection.
+     *
+     * @param requestType the request class
+     * @param replyType the reply class
+     * @param connectUri the URI to connect to
+     * @param <I> the request type
+     * @param <O> the reply type
+     * @return the future client
+     * @throws IllegalArgumentException if the provided URI scheme is not supported by this connector
+     */
+    <I, O> IoFuture<? extends Client<I, O>> openClient(Class<I> requestType, Class<O> replyType, URI connectUri) throws IllegalArgumentException;
+}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSource.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSource.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSource.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -22,25 +22,17 @@
 
 package org.jboss.remoting3;
 
-import java.io.IOException;
-
 /**
- * A source for new Remoting contexts.
+ * A source for new Remoting clients.
  *
  * @param <I> the request type
  * @param <O> the reply type
  */
 public interface ClientSource<I, O> extends HandleableCloseable<ClientSource<I, O>> {
     /**
-     * Close the context source.  New contexts may no longer be created after this
-     * method is called.  Subsequent calls to this method have no additional effect.
-     */
-    void close() throws IOException;
-
-    /**
-     * Create a new communications context.
+     * Create a new client instance.
      *
-     * @return the new context
+     * @return the client
      */
-    Client<I, O> createClient() throws IOException;
+    Client<I, O> createClient();
 }

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSourceConnector.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSourceConnector.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ClientSourceConnector.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+import java.net.URI;
+import org.jboss.xnio.IoFuture;
+
+/**
+ * A client source connector.  Opens a connection to a URI which provides a {@code ClientSource} instance.  Instances of this
+ * interface may only be able to support a single URI scheme.  Depending on the implementation, the URI may be a
+ * protocol URI or a service URI.
+ */
+public interface ClientSourceConnector extends HandleableCloseable<ClientSourceConnector> {
+
+    /**
+     * Establish a client source connection.
+     *
+     * @param requestType the request class
+     * @param replyType the reply class
+     * @param connectUri the URI to connect to
+     * @param <I> the request type
+     * @param <O> the reply type
+     * @return the future client
+     * @throws IllegalArgumentException if the provided URI scheme is not supported by this connector
+     */
+    <I, O> IoFuture<? extends ClientSource<I, O>> openClientSource(Class<I> requestType, Class<O> replyType, URI connectUri) throws IllegalArgumentException;
+}
\ No newline at end of file

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Endpoint.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Endpoint.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Endpoint.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -1,11 +1,14 @@
 package org.jboss.remoting3;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.net.URI;
+import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 import org.jboss.remoting3.spi.Handle;
 import org.jboss.remoting3.spi.RequestHandler;
 import org.jboss.remoting3.spi.RequestHandlerSource;
+import org.jboss.remoting3.spi.ConnectionProvider;
 import org.jboss.xnio.IoFuture;
 
 /**
@@ -45,7 +48,7 @@
      * @return a handle for the client
      * @throws IOException if an error occurs
      */
-    <I, O> Handle<RequestHandler> createRequestHandler(RequestListener<I, O> requestListener, final Class<I> requestClass, final Class<O> replyClass) throws IOException;
+    <I, O> Handle<RequestHandler> createLocalRequestHandler(RequestListener<I, O> requestListener, final Class<I> requestClass, final Class<O> replyClass) throws IOException;
 
     /**
      * Create a request handler source that can be used to acquire clients associated with a request listener on this endpoint.
@@ -62,6 +65,15 @@
     <I, O> Handle<RequestHandlerSource> registerService(LocalServiceConfiguration<I, O> configuration) throws IOException;
 
     /**
+     * Add a service registration listener which is called whenever a local service is registered.
+     *
+     * @param listener the listener
+     * @param flags the flags to apply to the listener
+     * @return a handle which may be used to remove the listener registration
+     */
+    SimpleCloseable addServiceRegistrationListener(ServiceRegistrationListener listener, Set<ListenerFlag> flags);
+
+    /**
      * Create a client that uses the given request handler to handle its requests.
      *
      * You must have the {@link org.jboss.remoting3.EndpointPermission createClient EndpointPermission} to invoke this method.
@@ -92,42 +104,63 @@
     <I, O> ClientSource<I, O> createClientSource(RequestHandlerSource handlerSource, Class<I> requestClass, Class<O> replyClass) throws IOException;
 
     /**
-     * Attempt to locate a service.  The return value then be queried for the service's {@code ClientSource}.
+     * Attempt to open a client source by URI.
      *
      * @param <I> the request type
      * @param <O> the reply type
-     * @param serviceUri the URI of the service
+     * @param uri the URI of the service
      * @param requestClass the class of requests sent through the client source
      * @param replyClass the class of replies received back through the client source
      * @return the future service
-     * @throws IllegalArgumentException if the given URI is not a valid Remoting service URI
+     * @throws IllegalArgumentException if the URI scheme does not correspond to a client souerce connection provider
      */
-    <I, O> IoFuture<ClientSource<I, O>> locateService(URI serviceUri, Class<I> requestClass, Class<O> replyClass) throws IllegalArgumentException;
+    <I, O> IoFuture<? extends ClientSource<I, O>> openClientSource(URI uri, Class<I> requestClass, Class<O> replyClass) throws IllegalArgumentException;
 
     /**
-     * Register a remotely available service.<p>
-     * The remote endpoint must not have the same name as this endpoint.  The group name and service type must be
-     * non-{@code null} and non-empty.  The metric must be greater than zero.
+     * Attempt to open a client by URI.
      *
-     * You must have the {@link org.jboss.remoting3.EndpointPermission registerRemoteService EndpointPermission} to invoke this method.
+     * @param <I> the request type
+     * @param <O> the reply type
+     * @param uri the URI of the service
+     * @param requestClass the class of requests sent through the client source
+     * @param replyClass the class of replies received back through the client source
+     * @return the future service
+     * @throws IllegalArgumentException if the URI scheme does not correspond to a client connection provider
+     */
+    <I, O> IoFuture<? extends Client<I, O>> openClient(URI uri, Class<I> requestClass, Class<O> replyClass) throws IllegalArgumentException;
+
+    /**
+     * Connect to a remote endpoint.
      *
-     * @param configuration the remote service configuration
-     * @return a closeable that may be used to remove the registration
-     * @throws IllegalArgumentException if one of the given arguments was not valid
-     * @throws IOException if an error occurs with the registration
+     * @param endpointUri the URI of the endpoint to connect to
+     * @return the future connection
+     * @throws IllegalArgumentException if the URI scheme does not correspond to an endpoint connection provider
      */
-    SimpleCloseable registerRemoteService(RemoteServiceConfiguration configuration) throws IllegalArgumentException, IOException;
+    IoFuture<? extends Closeable> openEndpointConnection(URI endpointUri) throws IllegalArgumentException;
 
     /**
-     * Add a listener for observing when local and remote services are added.  The caller may specify whether the listener
-     * should be notified of the complete list of currently registered services (set {@code onlyNew} to {@code false})
-     * or only services registered after the time of calling this method (set {@code onlyNew} to {@code true}).
+     * Register a connection provider for a URI scheme.
      *
-     * You must have the {@link org.jboss.remoting3.EndpointPermission addServiceListener EndpointPermission} to invoke this method.
+     * @param uriScheme the URI scheme
+     * @param provider the provider
+     * @return a handle which may be used to remove the registration
+     */
+    SimpleCloseable addConnectionProvider(String uriScheme, ConnectionProvider<?> provider);
+
+    /**
+     * Get the type of resource specified by the given URI.  If the type cannot be determined, returns {@link org.jboss.remoting3.ResourceType#UNKNOWN UNKNOWN}.
      *
-     * @param serviceListener the listener
-     * @param onlyNew {@code true} if only new registrations should be sent to the listener
-     * @return a handle which may be used to unregister the listener
+     * @param uri the connection URI
+     * @return the resource type
      */
-    SimpleCloseable addServiceListener(ServiceListener serviceListener, boolean onlyNew);
+    ResourceType getResourceType(URI uri);
+
+
+    enum ListenerFlag {
+
+        /**
+         * Include old registrations.
+         */
+        INCLUDE_OLD,
+    }
 }

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointConnector.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointConnector.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointConnector.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+import java.net.URI;
+import org.jboss.xnio.IoFuture;
+
+/**
+ * An endpoint connector.  Used to connect a whole endpoint to another whole endpoint.  Typically, services are then
+ * shared between the endpoints in some fashion, though this need not be the case.
+ */
+public interface EndpointConnector extends HandleableCloseable<EndpointConnector> {
+
+    /**
+     * Connect the given endpoint to the remote URI.
+     *
+     * @param endpoint the endpoint to connect
+     * @param connectUri the connection URI
+     * @return the future handle, which may be used to terminate the connection
+     */
+    IoFuture<? extends HandleableCloseable> connect(Endpoint endpoint, URI connectUri);
+}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointImpl.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointImpl.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/EndpointImpl.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -27,24 +27,31 @@
 import java.lang.ref.WeakReference;
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import org.jboss.remoting3.spi.AbstractHandleableCloseable;
 import org.jboss.remoting3.spi.AbstractSimpleCloseable;
+import org.jboss.remoting3.spi.ConnectionProvider;
 import org.jboss.remoting3.spi.Handle;
 import org.jboss.remoting3.spi.RequestHandler;
 import org.jboss.remoting3.spi.RequestHandlerSource;
-import org.jboss.xnio.FailedIoFuture;
-import org.jboss.xnio.FinishedIoFuture;
+import org.jboss.remoting3.spi.Cancellable;
+import org.jboss.remoting3.spi.EndpointConnection;
 import org.jboss.xnio.IoFuture;
 import org.jboss.xnio.IoUtils;
 import org.jboss.xnio.WeakCloseable;
+import org.jboss.xnio.AbstractIoFuture;
 import org.jboss.xnio.log.Logger;
 
 /**
@@ -57,6 +64,10 @@
         Logger.getLogger("org.jboss.remoting").info("JBoss Remoting version %s", Version.VERSION);
     }
 
+    static <K, V> ConcurrentMap<K, V> concurrentHashMap() {
+        return new ConcurrentHashMap<K, V>();
+    }
+
     static <K, V> Map<K, V> hashMap() {
         return new HashMap<K, V>();
     }
@@ -65,23 +76,38 @@
         return new HashSet<T>();
     }
 
+    static <T> Queue<T> concurrentLinkedQueue() {
+        return new ConcurrentLinkedQueue<T>();
+    }
+
     private static final Logger log = Logger.getLogger("org.jboss.remoting.endpoint");
 
     private final String name;
 
-    private final ConcurrentMap<Object, Object> endpointMap = new ConcurrentHashMap<Object, Object>();
+    /**
+     * Snapshot lock.  Hold this lock while reading or updating {@link #serviceListenerRegistrations} or while updating
+     * {@link #registeredLocalServices}.  Allows atomic snapshot of existing service registrations and listener add.
+     */
+    private final Lock serviceRegistrationLock = new ReentrantLock();
 
-    private final Object serviceLock = new Object();
-    private final Map<Object, ServiceListenerRegistration> serviceListenerMap = hashMap();
-    private final Set<ServiceRegistration> serviceRegistrations = hashSet();
+    /**
+     * 
+     */
+    private final Queue<Registration<ServiceRegistrationListener>> serviceListenerRegistrations = concurrentLinkedQueue();
 
+    private final ConcurrentMap<String, ServiceRegistration> registeredLocalServices = concurrentHashMap();
+
+    private final ConcurrentMap<String, ConnectionProvider<?>> connectionProviders = concurrentHashMap();
+
+    private final ConcurrentMap<Object, Object> endpointMap = concurrentHashMap();
+
     private static final EndpointPermission CREATE_ENDPOINT_PERM = new EndpointPermission("createEndpoint");
     private static final EndpointPermission CREATE_REQUEST_HANDLER_PERM = new EndpointPermission("createRequestHandler");
     private static final EndpointPermission REGISTER_SERVICE_PERM = new EndpointPermission("registerService");
     private static final EndpointPermission CREATE_CLIENT_PERM = new EndpointPermission("createClient");
     private static final EndpointPermission CREATE_CLIENT_SOURCE_PERM = new EndpointPermission("createClientSource");
-    private static final EndpointPermission REGISTER_REMOTE_SERVICE_PERM = new EndpointPermission("registerRemoteService");
     private static final EndpointPermission ADD_SERVICE_LISTENER_PERM = new EndpointPermission("addServiceListener");
+    private static final EndpointPermission ADD_CONNECTION_PROVIDER_PERM = new EndpointPermission("addConnectionProvider");
 
     public EndpointImpl(final Executor executor, final String name) {
         super(executor);
@@ -89,6 +115,7 @@
         if (sm != null) {
             sm.checkPermission(CREATE_ENDPOINT_PERM);
         }
+        connectionProviders.put("jrs", new JrsConnectionProvider());
         this.executor = executor;
         this.name = name;
     }
@@ -113,7 +140,7 @@
         return endpointMap;
     }
 
-    public <I, O> Handle<RequestHandler> createRequestHandler(final RequestListener<I, O> requestListener, final Class<I> requestClass, final Class<O> replyClass) throws IOException {
+    public <I, O> Handle<RequestHandler> createLocalRequestHandler(final RequestListener<I, O> requestListener, final Class<I> requestClass, final Class<O> replyClass) throws IOException {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             sm.checkPermission(CREATE_REQUEST_HANDLER_PERM);
@@ -146,21 +173,12 @@
         final String serviceType = configuration.getServiceType();
         final String groupName = configuration.getGroupName();
         final int metric = configuration.getMetric();
-        if (serviceType == null) {
-            throw new NullPointerException("serviceType is null");
-        }
-        if (groupName == null) {
-            throw new NullPointerException("groupName is null");
-        }
-        if (serviceType.length() == 0) {
-            throw new IllegalArgumentException("serviceType is empty");
-        }
-        if (groupName.length() == 0) {
-            throw new IllegalArgumentException("groupName is empty");
-        }
         if (metric < 0) {
             throw new IllegalArgumentException("metric must be greater than or equal to zero");
         }
+        ServiceURI.validateServiceType(serviceType);
+        ServiceURI.validateGroupName(groupName);
+        final String serviceKey = serviceType.toLowerCase() + ":" + groupName.toLowerCase();
         final LocalRequestHandlerSource.Config<I, O> config = new LocalRequestHandlerSource.Config<I,O>(configuration.getRequestClass(), configuration.getReplyClass());
         config.setRequestListener(configuration.getRequestListener());
         config.setExecutor(executor);
@@ -168,32 +186,21 @@
         final ServiceRegistration registration = new ServiceRegistration(serviceType, groupName, name, localRequestHandlerSource);
         final AbstractSimpleCloseable newHandle = new AbstractSimpleCloseable(executor) {
             protected void closeAction() throws IOException {
-                synchronized (serviceLock) {
-                    serviceRegistrations.remove(registration);
-                }
+                // todo fix
+                registeredLocalServices.remove(serviceKey);
             }
         };
         registration.setHandle(newHandle);
-        synchronized (serviceLock) {
-            serviceRegistrations.add(registration);
-            for (final ServiceListenerRegistration slr : serviceListenerMap.values()) {
-                final ServiceListener listener = slr.getServiceListener();
-                try {
-                    final ServiceListener.ServiceInfo serviceInfo = new ServiceListener.ServiceInfo();
-                    serviceInfo.setEndpointName(name);
-                    serviceInfo.setGroupName(groupName);
-                    serviceInfo.setServiceType(serviceType);
-                    serviceInfo.setMetric(metric);
-                    serviceInfo.setRegistrationHandle(newHandle);
-                    serviceInfo.setRemote(false);
-                    serviceInfo.setRequestHandlerSource(localRequestHandlerSource);
-                    listener.serviceRegistered(slr.handle, serviceInfo);
-                } catch (Throwable t) {
-                    logListenerError(t);
-                }
+        final Lock lock = serviceRegistrationLock;
+        lock.lock();
+        try {
+            if (registeredLocalServices.putIfAbsent(serviceKey, registration) != null) {
+                throw new ServiceRegistrationException("Registration of service of type \"" + serviceType + "\" in group \"" + groupName + "\" duplicates an already-registered service's specification");
             }
+        } finally {
+            lock.unlock();
         }
-        final WeakCloseable lrhCloseable = new WeakCloseable(new WeakReference<Closeable>(localRequestHandlerSource));
+        final WeakCloseable lrhCloseable = new WeakCloseable(localRequestHandlerSource);
         final Key key = addCloseHandler(new CloseHandler<Endpoint>() {
             public void handleClose(final Endpoint closed) {
                 IoUtils.safeClose(lrhCloseable);
@@ -205,6 +212,23 @@
             }
         });
         localRequestHandlerSource.open();
+        for (Registration<ServiceRegistrationListener> slr : serviceListenerRegistrations) {
+            final ServiceRegistrationListener registrationListener = slr.getResource();
+            try {
+                final ServiceRegistrationListener.ServiceInfo serviceInfo = new ServiceRegistrationListener.ServiceInfo();
+                serviceInfo.setGroupName(groupName);
+                serviceInfo.setServiceType(serviceType);
+                serviceInfo.setMetric(metric);
+                serviceInfo.setRegistrationHandle(newHandle);
+                serviceInfo.setRequestHandlerSource(localRequestHandlerSource);
+                registrationListener.serviceRegistered(slr, serviceInfo);
+            } catch (VirtualMachineError vme) {
+                // panic!
+                throw vme;
+            } catch (Throwable t) {
+                logListenerError(t);
+            }
+        }
         return localRequestHandlerSource.getHandle();
     }
 
@@ -270,204 +294,231 @@
         }
     }
 
-    public <I, O> IoFuture<ClientSource<I, O>> locateService(final URI serviceUri, final Class<I> requestClass, final Class<O> replyClass) throws IllegalArgumentException {
-        if (serviceUri == null) {
-            throw new NullPointerException("serviceUri is null");
+    public <I, O> IoFuture<ClientSource<I, O>> openClientSource(final URI uri, final Class<I> requestClass, final Class<O> replyClass) throws IllegalArgumentException {
+        final ConnectionProvider<RequestHandlerSource> cp = getConnectionProvider(uri);
+        if (cp.getResourceType() != ResourceType.CLIENT_SOURCE) {
+            throw new IllegalArgumentException("URI can not be used to open a client source");
         }
-        if (! ServiceURI.isRemotingServiceUri(serviceUri)) {
-            throw new IllegalArgumentException("Not a valid remoting service URI");
+        final FutureResult<ClientSource<I, O>> futureClientSource = new FutureResult<ClientSource<I, O>>();
+        cp.connect(uri, new ConnectionProvider.Result<RequestHandlerSource>() {
+            public void setResult(final RequestHandlerSource result) {
+                final ClientSource<I, O> clientSource;
+                try {
+                    clientSource = createClientSource(result, requestClass, replyClass);
+                } catch (IOException e) {
+                    IoUtils.safeClose(result);
+                    futureClientSource.setException(e);
+                    return;
+                }
+                futureClientSource.setResult(clientSource);
+            }
+
+            public void setException(final IOException exception) {
+                futureClientSource.setException(exception);
+            }
+
+            public void setCancelled() {
+                futureClientSource.finishCancel();
+            }
+        });
+        return futureClientSource;
+    }
+
+    public <I, O> IoFuture<? extends Client<I, O>> openClient(final URI uri, final Class<I> requestClass, final Class<O> replyClass) throws IllegalArgumentException {
+        final ConnectionProvider<RequestHandler> cp = getConnectionProvider(uri);
+        if (cp.getResourceType() != ResourceType.CLIENT) {
+            throw new IllegalArgumentException("URI can not be used to open a client");
         }
-        final String endpointName = ServiceURI.getEndpointName(serviceUri);
-        final String groupName = ServiceURI.getGroupName(serviceUri);
-        final String serviceType = ServiceURI.getServiceType(serviceUri);
-        synchronized (serviceLock) {
-            int bestMetric = Integer.MAX_VALUE;
-            List<ServiceRegistration> candidates = new ArrayList<ServiceRegistration>();
-            for (ServiceRegistration svc : serviceRegistrations) {
-                if (svc.matches(serviceType, groupName, endpointName)) {
-                    final int metric = svc.getMetric();
-                    if (metric < bestMetric) {
-                        candidates.clear();
-                        candidates.add(svc);
-                    } else if (metric == bestMetric) {
-                        candidates.add(svc);
-                    }
+        final FutureResult<Client<I, O>> futureClient = new FutureResult<Client<I, O>>();
+        cp.connect(uri, new ConnectionProvider.Result<RequestHandler>() {
+            public void setResult(final RequestHandler result) {
+                final Client<I, O> client;
+                try {
+                    client = createClient(result, requestClass, replyClass);
+                } catch (IOException e) {
+                    IoUtils.safeClose(result);
+                    futureClient.setException(e);
+                    return;
                 }
+                futureClient.setResult(client);
             }
-            final int size = candidates.size();
-            if (size == 0) {
-                final FutureClientSource<I, O> futureClientSource = new FutureClientSource<I, O>();
-                final SimpleCloseable listenerHandle = addServiceListener(new ServiceListener() {
-                    public void serviceRegistered(final SimpleCloseable listenerHandle, final ServiceInfo info) {
-                        final String addedEndpointName = info.getEndpointName();
-                        final String addedServiceType = info.getServiceType();
-                        final String addedGroupName = info.getGroupName();
-                        final RequestHandlerSource requestHandlerSource = info.getRequestHandlerSource();
-                        if (endpointName != null && endpointName.length() > 0 && !endpointName.equals(addedEndpointName)) {
-                            // no match
-                            return;
-                        }
-                        if (serviceType != null && serviceType.length() > 0 && !serviceType.equals(addedServiceType)) {
-                            // no match
-                            return;
-                        }
-                        if (groupName != null && groupName.length() > 0 && !groupName.equals(addedGroupName)) {
-                            // no match
-                            return;
-                        }
-                        try {
-                            // match!
-                            final ClientSource<I, O> clientSource = createClientSource(requestHandlerSource, requestClass, replyClass);
-                            futureClientSource.setResult(clientSource);
-                        } catch (IOException e) {
-                            futureClientSource.setException(e);
-                        } finally {
-                            IoUtils.safeClose(listenerHandle);
-                        }
+
+            public void setException(final IOException exception) {
+                futureClient.setException(exception);
+            }
+
+            public void setCancelled() {
+                futureClient.finishCancel();
+            }
+        });
+        return futureClient;
+    }
+
+    public IoFuture<? extends Closeable> openEndpointConnection(final URI endpointUri) throws IllegalArgumentException {
+        final ConnectionProvider<EndpointConnection> cp = getConnectionProvider(endpointUri);
+        if (cp.getResourceType() != ResourceType.CLIENT) {
+            throw new IllegalArgumentException("URI can not be used to open an endpoint connection");
+        }
+        final FutureResult<SimpleCloseable> futureEndpointConn = new FutureResult<SimpleCloseable>();
+        cp.connect(endpointUri, new ConnectionProvider.Result<EndpointConnection>() {
+            public void setResult(final EndpointConnection result) {
+                if (futureEndpointConn.setResult(new AbstractSimpleCloseable(executor) {
+                    protected void closeAction() throws IOException {
+                        result.close();
                     }
-                }, true);
-                futureClientSource.setListenerHandle(listenerHandle);
-                return futureClientSource;
+                })) {
+                    // todo - add the endpoint connection to the endpoint registry; notify listeners; etc.
+                }
             }
-            final RequestHandlerSource handlerSource;
-            if (size == 1) {
-                handlerSource = candidates.get(0).getHandlerSource();
-            } else {
-                int idx = (int) ((double) candidates.size() * Math.random());
-                handlerSource = candidates.get(idx).getHandlerSource();
+
+            public void setException(final IOException exception) {
+                futureEndpointConn.setException(exception);
             }
-            try {
-                return new FinishedIoFuture<ClientSource<I,O>>(createClientSource(handlerSource, requestClass, replyClass));
-            } catch (IOException e) {
-                return new FailedIoFuture<ClientSource<I,O>>(e);
+
+            public void setCancelled() {
+                futureEndpointConn.finishCancel();
             }
-        }
+        });
+        return futureEndpointConn;
     }
 
-    public SimpleCloseable registerRemoteService(final RemoteServiceConfiguration configuration) throws IllegalArgumentException, IOException {
-        SecurityManager sm = System.getSecurityManager();
+    public SimpleCloseable addConnectionProvider(final String uriScheme, final ConnectionProvider<?> provider) {
+        final SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            sm.checkPermission(REGISTER_REMOTE_SERVICE_PERM);
+            sm.checkPermission(ADD_CONNECTION_PROVIDER_PERM);
         }
-        final RequestHandlerSource handlerSource = configuration.getRequestHandlerSource();
-        final String serviceType = configuration.getServiceType();
-        final String groupName = configuration.getGroupName();
-        final String endpointName = configuration.getEndpointName();
-        final int metric = configuration.getMetric();
-        if (handlerSource == null) {
-            throw new NullPointerException("handlerSource is null");
+        final String key = uriScheme.toLowerCase();
+        if (connectionProviders.putIfAbsent(key, provider) != null) {
+            throw new IllegalArgumentException("Provider already registered for scheme \"" + uriScheme + "\"");
         }
-        if (serviceType == null) {
-            throw new NullPointerException("serviceType is null");
+        return new AbstractSimpleCloseable(executor) {
+            protected void closeAction() throws IOException {
+                connectionProviders.remove(key, provider);
+            }
+        };
+    }
+
+    public ResourceType getResourceType(final URI uri) {
+        final String scheme = uri.getScheme().toLowerCase();
+        final ConnectionProvider<?> provider = connectionProviders.get(scheme);
+        return provider != null ? provider.getResourceType() : ResourceType.UNKNOWN;
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    private <T> ConnectionProvider<T> getConnectionProvider(final URI uri) {
+        if (uri == null) {
+            throw new NullPointerException("serviceUri is null");
         }
-        if (groupName == null) {
-            throw new NullPointerException("groupName is null");
+        final String scheme = uri.getScheme();
+        // this cast is checked later, indirectly
+        final ConnectionProvider<T> cp = (ConnectionProvider<T>) connectionProviders.get(scheme);
+        if (cp == null) {
+            throw new IllegalArgumentException("No connection providers available for URI scheme \"" + scheme + "\"");
         }
-        if (endpointName == null) {
-            throw new NullPointerException("endpointName is null");
+        if (! ServiceURI.isRemotingServiceUri(uri)) {
+            throw new IllegalArgumentException("Not a valid remoting service URI");
         }
-        if (serviceType.length() == 0) {
-            throw new IllegalArgumentException("serviceType is empty");
+        return cp;
+    }
+
+    public SimpleCloseable addServiceRegistrationListener(final ServiceRegistrationListener listener, final Set<ListenerFlag> flags) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(ADD_SERVICE_LISTENER_PERM);
         }
-        if (groupName.length() == 0) {
-            throw new IllegalArgumentException("groupName is empty");
+        final Registration<ServiceRegistrationListener> registration = new Registration<ServiceRegistrationListener>(executor, listener, serviceListenerRegistrations);
+        final Lock lock = serviceRegistrationLock;
+        final Collection<ServiceRegistration> services;
+        lock.lock();
+        try {
+            if (flags == null || ! flags.contains(ListenerFlag.INCLUDE_OLD)) {
+                services = new ArrayList<ServiceRegistration>(registeredLocalServices.values());
+            } else {
+                services = Collections.emptySet();
+            }
+        } finally {
+            lock.unlock();
         }
-        if (endpointName.length() == 0) {
-            throw new IllegalArgumentException("endpointName is empty");
+        serviceListenerRegistrations.add(registration);
+        for (ServiceRegistration service : services) {
+            final ServiceRegistrationListener.ServiceInfo serviceInfo = new ServiceRegistrationListener.ServiceInfo();
+            serviceInfo.setGroupName(service.getGroupName());
+            serviceInfo.setMetric(service.getMetric());
+            serviceInfo.setRegistrationHandle(service.getHandle());
+            serviceInfo.setRequestHandlerSource(service.getHandlerSource());
+            serviceInfo.setServiceType(service.getServiceType());
+            listener.serviceRegistered(registration, serviceInfo);
+            if (! registration.isOpen()) {
+                break;
+            }
         }
-        if (endpointName.equals(name)) {
-            throw new IllegalArgumentException("remote endpoint has the same name as the local endpoint");
+        return registration;
+    }
+
+    private static final class Registration<T> extends AbstractSimpleCloseable {
+        private final T resource;
+        private final Queue<Registration<T>> resourceQueue;
+
+        private Registration(final Executor executor, final T resource, final Queue<Registration<T>> resourceQueue) {
+            super(executor);
+            this.resource = resource;
+            this.resourceQueue = resourceQueue;
         }
-        if (metric < 1) {
-            throw new IllegalArgumentException("metric must be greater than zero");
+
+        protected void closeAction() throws IOException {
+            resourceQueue.remove(this);
         }
-        final ServiceRegistration registration = new ServiceRegistration(serviceType, groupName, endpointName, metric, handlerSource);
-        final AbstractSimpleCloseable newHandle = new AbstractSimpleCloseable(executor) {
-            protected void closeAction() throws IOException {
-                synchronized (serviceLock) {
-                    serviceRegistrations.remove(registration);
-                }
-            }
-        };
-        registration.setHandle(newHandle);
-        synchronized (serviceLock) {
-            serviceRegistrations.add(registration);
-            for (final ServiceListenerRegistration slr : serviceListenerMap.values()) {
-                final ServiceListener listener = slr.getServiceListener();
-                try {
-                    final ServiceListener.ServiceInfo info = new ServiceListener.ServiceInfo();
-                    info.setEndpointName(endpointName);
-                    info.setGroupName(groupName);
-                    info.setMetric(metric);
-                    info.setRegistrationHandle(newHandle);
-                    info.setRemote(true);
-                    info.setRequestHandlerSource(handlerSource);
-                    info.setServiceType(serviceType);
-                    listener.serviceRegistered(slr.handle, info);
-                } catch (Throwable t) {
-                    logListenerError(t);
-                }
-            }
+
+        protected boolean isOpen() {
+            return super.isOpen();
         }
-        return newHandle;
+
+        T getResource() {
+            return resource;
+        }
     }
 
-    public SimpleCloseable addServiceListener(final ServiceListener serviceListener, final boolean onlyNew) {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(ADD_SERVICE_LISTENER_PERM);
-        }
-        final Object key = new Object();
-        synchronized (serviceLock) {
-            final ServiceListenerRegistration registration = new ServiceListenerRegistration(serviceListener);
-            serviceListenerMap.put(key, registration);
-            final AbstractSimpleCloseable handle = new AbstractSimpleCloseable(executor) {
-                protected void closeAction() throws IOException {
-                    synchronized (serviceLock) {
-                        serviceListenerMap.remove(key);
-                    }
+    public String toString() {
+        return "endpoint \"" + name + "\" <" + Integer.toHexString(hashCode()) + ">";
+    }
+
+    final class JrsConnectionProvider implements ConnectionProvider<RequestHandlerSource> {
+
+        public Cancellable connect(final URI uri, final Result<RequestHandlerSource> requestHandlerSourceResult) throws IllegalArgumentException {
+            final ServiceSpecification spec = ServiceSpecification.fromUri(uri);
+            for (ServiceRegistration sr : registeredLocalServices.values()) {
+                if (sr.matches(spec)) {
+                    requestHandlerSourceResult.setResult(sr.getHandlerSource());
                 }
-            };
-            registration.setHandle(handle);
-            if (! onlyNew) {
-                for (final ServiceRegistration reg : serviceRegistrations) {
-                    try {
-                        final ServiceListener.ServiceInfo info = new ServiceListener.ServiceInfo();
-                        info.setEndpointName(reg.getEndpointName());
-                        info.setGroupName(reg.getGroupName());
-                        info.setMetric(reg.getMetric());
-                        info.setRegistrationHandle(reg.getHandle());
-                        info.setRemote(reg.isRemote());
-                        info.setRequestHandlerSource(reg.getHandlerSource());
-                        info.setServiceType(reg.getServiceType());
-                        serviceListener.serviceRegistered(handle, info);
-                    } catch (Throwable t) {
-                        logListenerError(t);
-                    }
-                }
             }
-            return handle;
+            // todo - iterate through discovered services as well
+            return Cancellable.NULL_CANCELLABLE;
         }
+
+        public URI getConnectionUri(final URI uri) {
+            return uri;
+        }
+
+        public ResourceType getResourceType() {
+            return ResourceType.CLIENT_SOURCE;
+        }
     }
 
-    private static final class ServiceListenerRegistration {
-        private final ServiceListener serviceListener;
-        private volatile SimpleCloseable handle;
+    /**
+     *
+     */
+    static final class FutureResult<T> extends AbstractIoFuture<T> {
 
-        private ServiceListenerRegistration(final ServiceListener serviceListener) {
-            this.serviceListener = serviceListener;
+        protected boolean setException(final IOException exception) {
+            return super.setException(exception);
         }
 
-        ServiceListener getServiceListener() {
-            return serviceListener;
+        protected boolean setResult(final T result) {
+            return super.setResult(result);
         }
 
-        void setHandle(final SimpleCloseable handle) {
-            this.handle = handle;
+        protected boolean finishCancel() {
+            return super.finishCancel();
         }
     }
-
-    public String toString() {
-        return "endpoint \"" + name + "\" <" + Integer.toString(hashCode()) + ">";
-    }
 }

Deleted: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/FutureClientSource.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/FutureClientSource.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/FutureClientSource.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -1,53 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, 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.remoting3;
-
-import java.io.IOException;
-import org.jboss.xnio.AbstractIoFuture;
-import org.jboss.xnio.IoFuture;
-import org.jboss.xnio.IoUtils;
-
-/**
- *
- */
-final class FutureClientSource<I, O> extends AbstractIoFuture<ClientSource<I, O>> {
-
-    private volatile SimpleCloseable listenerHandle;
-
-    protected boolean setException(final IOException exception) {
-        return super.setException(exception);
-    }
-
-    protected boolean setResult(final ClientSource<I, O> result) {
-        return super.setResult(result);
-    }
-
-    public IoFuture<ClientSource<I, O>> cancel() {
-        IoUtils.safeClose(listenerHandle);
-        return this;
-    }
-
-    void setListenerHandle(final SimpleCloseable listenerHandle) {
-        this.listenerHandle = listenerHandle;
-    }
-}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/HandleableCloseable.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/HandleableCloseable.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/HandleableCloseable.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -35,15 +35,16 @@
 public interface HandleableCloseable<T> extends Closeable {
 
     /**
-     * Close, waiting for any outstanding processing to finish.
+     * Close this resource.  Call any registered close handlers.  Calling this method more than once will not have
+     * any additional effect.
      *
      * @throws IOException if the close failed
      */
     void close() throws IOException;
 
     /**
-     * Add a handler that will be called upon close.  The handler may be called before or after the close acutally
-     * takes place.
+     * Add a handler that will be called upon close.  If the resource is already closed, the handler will be called
+     * immediately.
      *
      * @param handler the close handler
      * @return a key which may be used to later remove this handler

Deleted: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RemoteServiceConfiguration.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RemoteServiceConfiguration.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RemoteServiceConfiguration.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -1,134 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, 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.remoting3;
-
-import org.jboss.remoting3.spi.RequestHandlerSource;
-
-/**
- * A configuration for registering a remote service with an endpoint.
- *
- * @apiviz.exclude
- */
-public final class RemoteServiceConfiguration {
-    private String serviceType;
-    private String groupName;
-    private String endpointName;
-    private RequestHandlerSource requestHandlerSource;
-    private int metric;
-
-    /**
-     * Construct a new instance.
-     */
-    public RemoteServiceConfiguration() {
-    }
-
-    /**
-     * Get the service type.
-     *
-     * @return the service type
-     */
-    public String getServiceType() {
-        return serviceType;
-    }
-
-    /**
-     * Set the service type.
-     *
-     * @param serviceType the service type
-     */
-    public void setServiceType(final String serviceType) {
-        this.serviceType = serviceType;
-    }
-
-    /**
-     * Get the service group name.
-     *
-     * @return the group name
-     */
-    public String getGroupName() {
-        return groupName;
-    }
-
-    /**
-     * Set the service group name.
-     *
-     * @param groupName the group name
-     */
-    public void setGroupName(final String groupName) {
-        this.groupName = groupName;
-    }
-
-    /**
-     * Get the remote endpoint name.
-     *
-     * @return the remote endpoint name
-     */
-    public String getEndpointName() {
-        return endpointName;
-    }
-
-    /**
-     * Set the remote endpoint name.
-     *
-     * @param endpointName the remote endpoint name
-     */
-    public void setEndpointName(final String endpointName) {
-        this.endpointName = endpointName;
-    }
-
-    /**
-     * Get the request handler source of the remote service.
-     *
-     * @return the request handler source
-     */
-    public RequestHandlerSource getRequestHandlerSource() {
-        return requestHandlerSource;
-    }
-
-    /**
-     * Set the request handler source of the remote service.
-     *
-     * @param requestHandlerSource the request handler source
-     */
-    public void setRequestHandlerSource(final RequestHandlerSource requestHandlerSource) {
-        this.requestHandlerSource = requestHandlerSource;
-    }
-
-    /**
-     * Get the metric of the remote service.
-     *
-     * @return the metric
-     */
-    public int getMetric() {
-        return metric;
-    }
-
-    /**
-     * Set the metric of the remote service.
-     *
-     * @param metric the metric
-     */
-    public void setMetric(final int metric) {
-        this.metric = metric;
-    }
-}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Remoting.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Remoting.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/Remoting.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -126,7 +126,7 @@
      * @throws IOException if an error occurs
      */
     public static <I, O> Client<I, O> createLocalClient(final Endpoint endpoint, final RequestListener<I, O> requestListener, final Class<I> requestClass, final Class<O> replyClass) throws IOException {
-        final Handle<RequestHandler> handle = endpoint.createRequestHandler(requestListener, requestClass, replyClass);
+        final Handle<RequestHandler> handle = endpoint.createLocalRequestHandler(requestListener, requestClass, replyClass);
         try {
             return endpoint.createClient(handle.getResource(), requestClass, replyClass);
         } finally {

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RequestListener.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RequestListener.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/RequestListener.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -49,8 +49,7 @@
      * Handle a request.  If this method throws {@code RemoteExecutionException}, then that exception is passed
      * back to the caller and the request is marked as complete.  Otherwise, the request
      * listener must send back either a reply (using the {@code sendReply()} method on the {@code RequestContext}) or
-     * an exception (using the {@code sendException()} method on the {@code RequestContext}).  Failure to do so may
-     * cause the client to hang indefinitely.
+     * an exception (using the {@code sendException()} method on the {@code RequestContext}).
      *
      * @param context the context on which a reply may be sent
      * @param request the received request

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ResourceType.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ResourceType.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ResourceType.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+/**
+ *
+ */
+public enum ResourceType {
+    UNKNOWN,
+    CLIENT,
+    CLIENT_SOURCE,
+    ENDPOINT,
+}

Deleted: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceListener.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceListener.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceListener.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -1,188 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, 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.remoting3;
-
-import org.jboss.remoting3.spi.RequestHandlerSource;
-
-/**
- * A listener for watching service registrations on an endpoint.
- *
- * @apiviz.landmark
- */
-public interface ServiceListener {
-
-    /**
-     * Receive notification that a service was registered.
-     *
-     * @param listenerHandle the handle to this listener
-     * @param info the servce information
-     */
-    void serviceRegistered(SimpleCloseable listenerHandle, ServiceInfo info);
-
-    /**
-     * Information about a registered service.
-     *
-     * @apiviz.exclude
-     */
-    final class ServiceInfo {
-        private String endpointName;
-        private String serviceType;
-        private String groupName;
-        private boolean remote;
-        private int metric;
-        private RequestHandlerSource requestHandlerSource;
-        private SimpleCloseable registrationHandle;
-
-        /**
-         * Construct a new instance.
-         */
-        public ServiceInfo() {
-        }
-
-        /**
-         * Get the service type.
-         *
-         * @return the service type
-         */
-        public String getServiceType() {
-            return serviceType;
-        }
-
-        /**
-         * Set the service type.
-         *
-         * @param serviceType the service type
-         */
-        public void setServiceType(final String serviceType) {
-            this.serviceType = serviceType;
-        }
-
-        /**
-         * Get the group name.
-         *
-         * @return the group name
-         */
-        public String getGroupName() {
-            return groupName;
-        }
-
-        /**
-         * Set the group name.
-         *
-         * @param groupName the group name
-         */
-        public void setGroupName(final String groupName) {
-            this.groupName = groupName;
-        }
-
-        /**
-         * Get the metric.
-         *
-         * @return the metric
-         */
-        public int getMetric() {
-            return metric;
-        }
-
-        /**
-         * Set the metric.
-         *
-         * @param metric the metric
-         */
-        public void setMetric(final int metric) {
-            this.metric = metric;
-        }
-
-        /**
-         * Get the request handler source.
-         *
-         * @return the request handler source
-         */
-        public RequestHandlerSource getRequestHandlerSource() {
-            return requestHandlerSource;
-        }
-
-        /**
-         * Set the request handler source.
-         *
-         * @param requestHandlerSource the request handler source
-         */
-        public void setRequestHandlerSource(final RequestHandlerSource requestHandlerSource) {
-            this.requestHandlerSource = requestHandlerSource;
-        }
-
-        /**
-         * Get the registration handle.  Closing this handle will remove the registration.
-         *
-         * @return the registration handle
-         */
-        public SimpleCloseable getRegistrationHandle() {
-            return registrationHandle;
-        }
-
-        /**
-         * Set the registration handle.
-         *
-         * @param registrationHandle the registration handle
-         */
-        public void setRegistrationHandle(final SimpleCloseable registrationHandle) {
-            this.registrationHandle = registrationHandle;
-        }
-
-        /**
-         * Get the endpoint name.  For local services, this will be the name of the local endpoint.
-         *
-         * @return the endpoint name
-         */
-        public String getEndpointName() {
-            return endpointName;
-        }
-
-        /**
-         * Set the endpoint name.
-         *
-         * @param endpointName the endpoint name
-         */
-        public void setEndpointName(final String endpointName) {
-            this.endpointName = endpointName;
-        }
-
-        /**
-         * Determine whether this service is remote.
-         *
-         * @return {@code true} if this service is remote
-         */
-        public boolean isRemote() {
-            return remote;
-        }
-
-        /**
-         * Specify whether this service is remote.
-         *
-         * @param remote {@code true} if this service is remote
-         */
-        public void setRemote(final boolean remote) {
-            this.remote = remote;
-        }
-    }
-}

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceLocationListener.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceLocationListener.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceLocationListener.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+import java.net.URI;
+
+/**
+ * A listener for watching service location events on an endpoint.
+ *
+ * @apiviz.landmark
+ */
+public interface ServiceLocationListener {
+    void serviceLocated(SimpleCloseable listenerHandler, ServiceInfo info);
+
+    final class ServiceInfo {
+        private URI serviceUri;
+        private URI locationUri;
+        private int preference;
+
+        public URI getServiceUri() {
+            return serviceUri;
+        }
+
+        public void setServiceUri(final URI serviceUri) {
+            this.serviceUri = serviceUri;
+        }
+
+        public URI getLocationUri() {
+            return locationUri;
+        }
+
+        public void setLocationUri(final URI locationUri) {
+            this.locationUri = locationUri;
+        }
+
+        public int getPreference() {
+            return preference;
+        }
+
+        public void setPreference(final int preference) {
+            this.preference = preference;
+        }
+    }
+}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistration.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistration.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistration.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -55,11 +55,18 @@
     }
 
     public boolean matches(final String serviceType, final String groupName, final String endpointName) {
-        return  (serviceType == null || serviceType.length() == 0 || serviceType.equals(this.serviceType)) &&
-                (groupName == null || groupName.length() == 0 || groupName.equals(this.groupName)) &&
-                (endpointName == null || endpointName.length() == 0 || endpointName.equals(this.endpointName));
+        return  (serviceType == null || serviceType.length() == 0 || "*".equals(serviceType) || serviceType.equals(this.serviceType)) &&
+                (groupName == null || groupName.length() == 0 || "*".equals(groupName) || groupName.equals(this.groupName)) &&
+                (endpointName == null || endpointName.length() == 0 || "*".equals(endpointName) || endpointName.equals(this.endpointName));
     }
 
+    public boolean matches(final ServiceSpecification spec) {
+        final String serviceType = spec.getServiceType();
+        final String groupName = spec.getGroupName();
+        final String endpointName = spec.getEndpointName();
+        return matches(serviceType, groupName, endpointName);
+    }
+
     public boolean isRemote() {
         return remote;
     }

Copied: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistrationListener.java (from rev 4903, remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceListener.java)
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistrationListener.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceRegistrationListener.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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.remoting3;
+
+import org.jboss.remoting3.spi.RequestHandlerSource;
+
+/**
+ * A listener for watching service registrations on an endpoint.
+ *
+ * @apiviz.landmark
+ */
+public interface ServiceRegistrationListener {
+
+    /**
+     * Receive notification that a service was registered.
+     *
+     * @param listenerHandle the handle to this listener
+     * @param info the servce information
+     */
+    void serviceRegistered(SimpleCloseable listenerHandle, ServiceInfo info);
+
+    /**
+     * Information about a registered service.
+     *
+     * @apiviz.exclude
+     */
+    final class ServiceInfo {
+        private String serviceType;
+        private String groupName;
+        private int metric;
+        private RequestHandlerSource requestHandlerSource;
+        private SimpleCloseable registrationHandle;
+
+        /**
+         * Construct a new instance.
+         */
+        public ServiceInfo() {
+        }
+
+        /**
+         * Get the service type.
+         *
+         * @return the service type
+         */
+        public String getServiceType() {
+            return serviceType;
+        }
+
+        /**
+         * Set the service type.
+         *
+         * @param serviceType the service type
+         */
+        public void setServiceType(final String serviceType) {
+            this.serviceType = serviceType;
+        }
+
+        /**
+         * Get the group name.
+         *
+         * @return the group name
+         */
+        public String getGroupName() {
+            return groupName;
+        }
+
+        /**
+         * Set the group name.
+         *
+         * @param groupName the group name
+         */
+        public void setGroupName(final String groupName) {
+            this.groupName = groupName;
+        }
+
+        /**
+         * Get the metric.
+         *
+         * @return the metric
+         */
+        public int getMetric() {
+            return metric;
+        }
+
+        /**
+         * Set the metric.
+         *
+         * @param metric the metric
+         */
+        public void setMetric(final int metric) {
+            this.metric = metric;
+        }
+
+        /**
+         * Get the request handler source.
+         *
+         * @return the request handler source
+         */
+        public RequestHandlerSource getRequestHandlerSource() {
+            return requestHandlerSource;
+        }
+
+        /**
+         * Set the request handler source.
+         *
+         * @param requestHandlerSource the request handler source
+         */
+        public void setRequestHandlerSource(final RequestHandlerSource requestHandlerSource) {
+            this.requestHandlerSource = requestHandlerSource;
+        }
+
+        /**
+         * Get the registration handle.  Closing this handle will remove the registration.
+         *
+         * @return the registration handle
+         */
+        public SimpleCloseable getRegistrationHandle() {
+            return registrationHandle;
+        }
+
+        /**
+         * Set the registration handle.
+         *
+         * @param registrationHandle the registration handle
+         */
+        public void setRegistrationHandle(final SimpleCloseable registrationHandle) {
+            this.registrationHandle = registrationHandle;
+        }
+    }
+}

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceSpecification.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceSpecification.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceSpecification.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3;
+
+import java.net.URI;
+
+/**
+ * A specification of a JBoss Remoting service.
+ *
+ * @apiviz.exclude
+ */
+public final class ServiceSpecification {
+    private final String serviceType;
+    private final String groupName;
+    private final String endpointName;
+
+    public ServiceSpecification(final String serviceType, final String groupName, final String endpointName) {
+        if (serviceType == null) {
+            throw new NullPointerException("serviceType is null");
+        }
+        if (groupName == null) {
+            throw new NullPointerException("groupName is null");
+        }
+        if (endpointName == null) {
+            throw new NullPointerException("endpointName is null");
+        }
+        if (serviceType.length() > 0 && ! "*".equals(serviceType)) {
+            ServiceURI.validateServiceType(serviceType);
+            this.serviceType = serviceType.toLowerCase();
+        } else {
+            this.serviceType = "*";
+        }
+        if (groupName.length() > 0 && ! "*".equals(groupName)) {
+            ServiceURI.validateGroupName(groupName);
+            this.groupName = groupName.toLowerCase();
+        } else {
+            this.groupName = "*";
+        }
+        if (endpointName.length() > 0 && ! "*".equals(endpointName)) {
+            ServiceURI.validateEndpointName(endpointName);
+            this.endpointName = endpointName.toLowerCase();
+        } else {
+            this.endpointName = "*";
+        }
+    }
+
+    public static ServiceSpecification fromUri(final URI uri) {
+        return new ServiceSpecification(ServiceURI.getServiceType(uri), ServiceURI.getGroupName(uri), ServiceURI.getEndpointName(uri));
+    }
+
+    public String getServiceType() {
+        return serviceType;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public String getEndpointName() {
+        return endpointName;
+    }
+
+    public boolean matches(final URI serviceUri) {
+        if (! ServiceURI.isRemotingServiceUri(serviceUri)) {
+            return false;
+        }
+        if (! "*".equals(serviceType)) {
+            if (! serviceType.equals(ServiceURI.getServiceType(serviceUri))) {
+                return false;
+            }
+        }
+        if (! "*".equals(groupName)) {
+            if (! groupName.equals(ServiceURI.getGroupName(serviceUri))) {
+                return false;
+            }
+        }
+        if (! "*".equals(endpointName)) {
+            if (! endpointName.equals(ServiceURI.getEndpointName(serviceUri))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (! (o instanceof ServiceSpecification)) return false;
+        final ServiceSpecification that = (ServiceSpecification) o;
+        if (!endpointName.equals(that.endpointName)) return false;
+        if (!groupName.equals(that.groupName)) return false;
+        if (!serviceType.equals(that.serviceType)) return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = serviceType.hashCode();
+        result = 31 * result + groupName.hashCode();
+        result = 31 * result + endpointName.hashCode();
+        return result;
+    }
+}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceURI.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceURI.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/ServiceURI.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -24,6 +24,7 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.regex.Pattern;
 
 /**
  * A parser for JBoss Remoting URI types.
@@ -65,7 +66,7 @@
         } else {
             serviceType = ssp.substring(0, firstColon);
         }
-        return serviceType;
+        return serviceType.toLowerCase();
     }
 
     /**
@@ -91,7 +92,7 @@
         } else {
             groupName = ssp.substring(firstColon + 1, secondColon);
         }
-        return groupName;
+        return groupName.toLowerCase();
     }
 
     /**
@@ -122,7 +123,7 @@
         } else {
             endpointName = ssp.substring(secondColon + 1, thirdColon);
         }
-        return endpointName;
+        return endpointName.toLowerCase();
     }
 
     /**
@@ -152,4 +153,35 @@
             throw new IllegalStateException("URI syntax exception should not be possible here", e);
         }
     }
+
+    private static final Pattern VALID_SERVICE_TYPE = Pattern.compile("^(?:[_a-z][_a-z0-9]*)(?:\\.[_a-z][_a-z0-9]*)*$", Pattern.CASE_INSENSITIVE);
+    private static final Pattern VALID_GROUP_NAME = Pattern.compile("^(?:[_a-z0-9]+)(?:\\.[_a-z0-9]+)*$", Pattern.CASE_INSENSITIVE);
+    private static final Pattern VALID_ENDPOINT_NAME = VALID_SERVICE_TYPE;
+
+    public static void validateServiceType(final CharSequence serviceType) {
+        if (serviceType == null) {
+            throw new NullPointerException("serviceType is null");
+        }
+        if (! VALID_SERVICE_TYPE.matcher(serviceType).matches()) {
+            throw new IllegalArgumentException("Service type \"" + serviceType + "\" is not valid");
+        }
+    }
+
+    public static void validateGroupName(final CharSequence groupName) {
+        if (groupName == null) {
+            throw new NullPointerException("groupName is null");
+        }
+        if (! VALID_GROUP_NAME.matcher(groupName).matches()) {
+            throw new IllegalArgumentException("Group name \"" + groupName + "\" is not valid");
+        }
+    }
+
+    public static void validateEndpointName(final String endpointName) {
+        if (endpointName == null) {
+            throw new NullPointerException("endpointName is null");
+        }
+        if (! VALID_ENDPOINT_NAME.matcher(endpointName).matches()) {
+            throw new IllegalArgumentException("Endpoint name \"" + endpointName + "\" is not valid");
+        }
+    }
 }

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/Cancellable.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/Cancellable.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/Cancellable.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3.spi;
+
+/**
+ * A handle which can be used to cancel an operation.  Cancellation is not mandatory; calling this method merely indicates
+ * that the operation need not complete.
+ */
+public interface Cancellable {
+
+    /**
+     * Cancel the operation.  Calling this method more than one time has no additional effect.
+     */
+    void cancel();
+
+    /**
+     * A Cancellable instance which does nothing.
+     */
+    Cancellable NULL_CANCELLABLE = new Cancellable() {
+        public void cancel() {
+        }
+    };
+}

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/ConnectionProvider.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/ConnectionProvider.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/ConnectionProvider.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3.spi;
+
+import java.io.IOException;
+import java.net.URI;
+import org.jboss.remoting3.ResourceType;
+
+/**
+ *
+ */
+public interface ConnectionProvider<T> {
+    Cancellable connect(URI uri, Result<T> result) throws IllegalArgumentException;
+
+    URI getConnectionUri(URI uri);
+
+    ResourceType getResourceType();
+
+    interface Result<T> {
+        void setResult(T result);
+
+        void setException(IOException exception);
+
+        void setCancelled();
+    }
+}

Added: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/EndpointConnection.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/EndpointConnection.java	                        (rev 0)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/EndpointConnection.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.remoting3.spi;
+
+import java.io.Closeable;
+
+/**
+ * A connection to a foreign endpoint.
+ */
+public interface EndpointConnection extends Closeable {
+    String getEndpointName();
+
+    int getMetric();
+
+    ConnectionProvider<RequestHandler> getRequestHandlerConnectionProvider();
+
+    ConnectionProvider<RequestHandlerSource> getRequestHandlerSourceConnectionProvider();
+}

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RemoteRequestContext.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RemoteRequestContext.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RemoteRequestContext.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -26,7 +26,7 @@
  * The context of an outstanding remote request.  This instance should be discarded when a reply (of any sort)
  * is received for the request.
  */
-public interface RemoteRequestContext {
+public interface RemoteRequestContext extends Cancellable {
 
     /**
      * Signal that the request should be cancelled, if possible.

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandler.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandler.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandler.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -22,9 +22,6 @@
 
 package org.jboss.remoting3.spi;
 
-import java.io.IOException;
-import org.jboss.remoting3.CloseHandler;
-
 /**
  * A request handler, which can be passed to remote endpoints.  Remote systems can then use the handler
  * to make invocations, or they may forward a handler on to other remote systems.
@@ -46,30 +43,4 @@
      * @return a context which may be used to cancel the request
      */
     RemoteRequestContext receiveRequest(Object request, ReplyHandler replyHandler);
-
-    /**
-     * Get a handle to this request handler.  The request handler will not auto-close as long as there is at least
-     * one open handle.  If a handle is "leaked", it will be closed
-     * automatically if/when the garbage collector invokes its {@link Object#finalize()} method, with a log message
-     * warning of the leak.
-     *
-     * @return the handle
-     * @throws IOException if a handle could not be acquired
-     */
-    Handle<RequestHandler> getHandle() throws IOException;
-
-    /**
-     * Close this request handler.  The outcome of any outstanding requests is not defined, though implementations
-     * should make an effort to cancel any outstanding requests.
-     *
-     * @throws java.io.IOException if the client endpoint could not be closed
-     */
-    void close() throws IOException;
-
-    /**
-     * Add a handler that is called when the request handler is closed.
-     *
-     * @param handler the handler to be called
-     */
-    Key addCloseHandler(final CloseHandler<? super RequestHandler> handler);
 }

Modified: remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandlerSource.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandlerSource.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/main/java/org/jboss/remoting3/spi/RequestHandlerSource.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -23,8 +23,6 @@
 package org.jboss.remoting3.spi;
 
 import java.io.IOException;
-import org.jboss.remoting3.CloseHandler;
-import org.jboss.remoting3.RemotingException;
 
 /**
  * A request handler source, which can be passed to remote endpoints.  Remote systems can then use the handler source
@@ -38,30 +36,7 @@
      * Create a request handler for the service corresponding to this request handler source.
      *
      * @return a request handler
-     * @throws RemotingException if a client could not be opened
+     * @throws IOException if a client could not be opened
      */
     Handle<RequestHandler> createRequestHandler() throws IOException;
-
-    /**
-     * Get a handle to this request handler source.  The request handler source will not auto-close as long as there is at least
-     * one open handle, or request handler.  If a handle is "leaked", it will be closed
-     * automatically if/when the garbage collector invokes its {@link Object#finalize()} method, with a log message
-     * warning of the leak.
-     *
-     * @return the handle
-     * @throws RemotingException if a handle could not be acquired
-     */
-    Handle<RequestHandlerSource> getHandle() throws IOException;
-
-    /**
-     * Close this request handler source immediately.
-     */
-    void close() throws IOException;
-
-    /**
-     * Add a handler that is called when the request handler source is closed.
-     *
-     * @param handler the handler to be called
-     */
-    Key addCloseHandler(final CloseHandler<? super RequestHandlerSource> handler);
 }

Modified: remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/EndpointTestCase.java
===================================================================
--- remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/EndpointTestCase.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/jboss-remoting/src/test/java/org/jboss/remoting3/EndpointTestCase.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -68,7 +68,7 @@
             final Object requestObj = new Object();
             final Object replyObj = new Object();
             try {
-                final Handle<RequestHandler> handle = endpoint.createRequestHandler(new AbstractRequestListener<Object, Object>() {
+                final Handle<RequestHandler> handle = endpoint.createLocalRequestHandler(new AbstractRequestListener<Object, Object>() {
                     public void handleRequest(final RequestContext<Object> context, final Object request) throws RemoteExecutionException {
                         assertEquals(request, requestObj);
                         try {
@@ -126,7 +126,7 @@
             try {
                 final Object requestObj = new Object();
                 final Object replyObj = new Object();
-                final Handle<RequestHandler> handle = endpoint.createRequestHandler(new AbstractRequestListener<Object, Object>() {
+                final Handle<RequestHandler> handle = endpoint.createLocalRequestHandler(new AbstractRequestListener<Object, Object>() {
                     public void handleRequest(final RequestContext<Object> context, final Object request) throws RemoteExecutionException {
                         assertEquals(request, requestObj);
                         try {
@@ -183,7 +183,7 @@
             final EndpointImpl endpoint = new EndpointImpl(executorService, "test-endpoint");
             try {
                 final Object requestObj = new Object();
-                final Handle<RequestHandler> handle = endpoint.createRequestHandler(new AbstractRequestListener<Object, Object>() {
+                final Handle<RequestHandler> handle = endpoint.createLocalRequestHandler(new AbstractRequestListener<Object, Object>() {
                     public void handleRequest(final RequestContext<Object> context, final Object request) throws RemoteExecutionException {
                         assertEquals(request, requestObj);
                         // don't send a reply!!
@@ -220,7 +220,7 @@
             final EndpointImpl endpoint = new EndpointImpl(executorService, "test-endpoint");
             try {
                 final Object requestObj = new Object();
-                final Handle<RequestHandler> handle = endpoint.createRequestHandler(new AbstractRequestListener<Object, Object>() {
+                final Handle<RequestHandler> handle = endpoint.createLocalRequestHandler(new AbstractRequestListener<Object, Object>() {
                     public void handleRequest(final RequestContext<Object> context, final Object request) throws RemoteExecutionException {
                         assertEquals(request, requestObj);
                         context.execute(new Runnable() {

Modified: remoting3/trunk/samples/src/test/java/org/jboss/remoting3/samples/protocol/basic/BasicTestCase.java
===================================================================
--- remoting3/trunk/samples/src/test/java/org/jboss/remoting3/samples/protocol/basic/BasicTestCase.java	2009-04-14 10:34:23 UTC (rev 5031)
+++ remoting3/trunk/samples/src/test/java/org/jboss/remoting3/samples/protocol/basic/BasicTestCase.java	2009-04-14 17:18:15 UTC (rev 5032)
@@ -63,7 +63,7 @@
                 configuration.setMarshallingConfiguration(marshallingConfiguration);
                 final Endpoint endpoint = Remoting.createEndpoint(executor, "test");
                 try {
-                    final Handle<RequestHandler> requestHandlerHandle = endpoint.createRequestHandler(new AbstractRequestListener<Object, Object>() {
+                    final Handle<RequestHandler> requestHandlerHandle = endpoint.createLocalRequestHandler(new AbstractRequestListener<Object, Object>() {
                         public void handleRequest(final RequestContext<Object> context, final Object request) throws RemoteExecutionException {
                             System.out.println("Got a request! " + request.toString());
                             try {




More information about the jboss-remoting-commits mailing list