You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2007/03/25 22:04:41 UTC

svn commit: r522339 - in /incubator/openejb/trunk/openejb3/server/openejb-client/src/main: java/org/apache/openejb/client/ resources/META-INF/ resources/META-INF/org.apache.openejb.client.IdentityResolver/

Author: dain
Date: Sun Mar 25 13:04:39 2007
New Revision: 522339

URL: http://svn.apache.org/viewvc?view=rev&rev=522339
Log:
Added copy of ResourceFinder to client package
Added finder based client identity resolution strategy for associating an identity token with server requests.
Added JAAS strategy
Added simple login methods to ClientSecurity and associated resolution strategy

Added:
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientSecurity.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/IdentityResolver.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ResourceFinder.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/jaas
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/simple
Modified:
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBHomeHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBHomeHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBObjectHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBHomeHandler.java
    incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBObjectHandler.java

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientLoginModule.java Sun Mar 25 13:04:39 2007
@@ -24,12 +24,10 @@
 import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginException;
-import javax.security.auth.login.FailedLoginException;
 import javax.security.auth.spi.LoginModule;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.rmi.RemoteException;
 import java.util.Map;
 import java.util.logging.Logger;
 
@@ -93,21 +91,7 @@
         char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
         if (tmpPassword == null) tmpPassword = new char[0];
 
-        // authenticate
-        AuthenticationRequest authReq = new AuthenticationRequest(user, new String(tmpPassword));
-        AuthenticationResponse authRes;
-        try {
-            authRes = (AuthenticationResponse) Client.request(authReq, new AuthenticationResponse(), server);
-        } catch (RemoteException e) {
-            throw (LoginException) new LoginException("Unable to authenticate with server " + serverUri).initCause(e);
-        }
-
-        // check the response
-        if (authRes.getResponseCode() == ResponseCodes.AUTH_GRANTED) {
-            clientIdentity = authRes.getIdentity().getClientIdentity();
-        } else {
-            throw new FailedLoginException("This principle is not authorized.");
-        }
+        clientIdentity = ClientSecurity.directAuthentication(user, new String(tmpPassword), server);
 
         if (debug) {
             log.config("login " + user);

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientSecurity.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientSecurity.java?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientSecurity.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ClientSecurity.java Sun Mar 25 13:04:39 2007
@@ -0,0 +1,199 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.client;
+
+import javax.security.auth.login.FailedLoginException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.rmi.RemoteException;
+
+public class ClientSecurity {
+    public static final String IDENTITY_RESOLVER_STRATEGY = "openejb.client.identityResolver";
+
+    private static ServerMetaData server;
+    private static IdentityResolver identityResolver;
+    private static Object staticClientIdentity;
+    private static InheritableThreadLocal<Object> threadClientIdentity = new InheritableThreadLocal<Object>();
+
+    static {
+        // determine the server uri
+        String serverUri = System.getProperty("openejb.server.uri");
+
+        if (serverUri != null) {
+            // determine the server location
+            try {
+                URI location = new URI(serverUri);
+                server = new ServerMetaData(location);
+            } catch (Exception e) {
+                if (serverUri.indexOf("://") == -1) {
+                    try {
+                        URI location =  new URI("oejb://" + serverUri);
+                        server = new ServerMetaData(location);
+                    } catch (URISyntaxException ignored) {
+                    }
+                }
+            }
+        }
+    }
+
+
+    public static ServerMetaData getServer() {
+        return server;
+    }
+
+    public static void setServer(ServerMetaData server) {
+        ClientSecurity.server = server;
+    }
+
+    /**
+     * Login the spedified user using the specified password.  This is a global login for the
+     * entire Java Virtural Machine.  If you would like to have a thread scoped login, use
+     * ClientSecurity.login(username, password, true);
+     * </p>
+     * This is the equivalent of ClientSecurity.login(username, password, false);
+     * @param username the user to login
+     * @param password the password for the user
+     * @throws FailedLoginException if the username and password combination are not valid or
+     *      if there is a problem communiating with the server
+     */
+    public static void login(String username, String password) throws FailedLoginException {
+        login(username, password, false);
+    }
+
+    /**
+     * Login the spedified user using the specified password either globally for the
+     * entire Java Virtural Machine or scoped to the thread.
+     * </p>
+     * When using thread scoped login, you should logout in a finally block.  This particularly
+     * when using thread pools.  If a thread is returned to the pool with a login attached to the
+     * thread the next user of that thread will inherit the thread scoped login.
+     *
+     * @param username the user to login
+     * @param password the password for the user
+     * @param threadScoped if true the login is scoped to the thread; otherwise the login is global
+     * for the entire Java Virtural Machine
+     * @throws FailedLoginException if the username and password combination are not valid or
+     *      if there is a problem communiating with the server
+     */
+    public static void login(String username, String password, boolean threadScoped) throws FailedLoginException {
+        Object clientIdentity = directAuthentication(username, password, server);
+        if (threadScoped) {
+            threadClientIdentity.set(clientIdentity);
+        } else {
+            staticClientIdentity = clientIdentity;
+        }
+        identityResolver = new SimpleIdentityResolver();
+    }
+
+    /**
+     * Clears the thread and global login data.
+     */
+    public static void logout() {
+        threadClientIdentity.set(null);
+        staticClientIdentity = null;
+    }
+
+    /**
+     * This is a helper method for login modules. Directly authenticates with the server using the specified
+     * username and password returning the identity token for the client.  This methods does not store the
+     * identity token and the caller must arrange for the to be available to the OpenEJB proxies via an
+     * IdentityResolver.
+     *
+     * @param username the username for authentication
+     * @param password the password for authentication
+     * @param server
+     * @return the client identity token
+     * @throws FailedLoginException if the username password combination is not valid
+     */
+    public static Object directAuthentication(String username, String password, ServerMetaData server) throws FailedLoginException {
+        // authenticate
+        AuthenticationRequest authReq = new AuthenticationRequest(username, password);
+        AuthenticationResponse authRes;
+        try {
+            authRes = (AuthenticationResponse) Client.request(authReq, new AuthenticationResponse(), server);
+        } catch (RemoteException e) {
+            throw (FailedLoginException) new FailedLoginException("Unable to authenticate with server " + server).initCause(e);
+        }
+
+        // check the response
+        if (authRes.getResponseCode() != ResponseCodes.AUTH_GRANTED) {
+            throw new FailedLoginException("This principle is not authorized.");
+        }
+
+        // return the response object
+        Object clientIdentity = authRes.getIdentity().getClientIdentity();
+        return clientIdentity;
+    }
+
+    public static Object getIdentity() {
+        return getIdentityResolver().getIdentity();
+    }
+
+    public static IdentityResolver getIdentityResolver() {
+        if (identityResolver == null) {
+            String strategy = System.getProperty(IDENTITY_RESOLVER_STRATEGY);
+            if (strategy == null) {
+                identityResolver = new JaasIdentityResolver();
+            } else {
+                // find the strategy class
+                ResourceFinder finder = new ResourceFinder("META-INF/");
+                Class identityResolverClass;
+                try {
+                    identityResolverClass = finder.findClass(IdentityResolver.class.getName() + "/" + strategy);
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("Could not find client identity strategy '" + strategy + "'");
+                }
+
+                // verify the interface
+                if (!IdentityResolver.class.isAssignableFrom(identityResolverClass)) {
+                    throw new IllegalArgumentException("Client identity strategy '" + strategy + "' " +
+                            "class '" + identityResolverClass.getName() + "' does not implement the " +
+                            "interface '"  + IdentityResolver.class.getSimpleName() + "'");
+                }
+
+                // create the class
+                try {
+                    identityResolver = (IdentityResolver) identityResolverClass.newInstance();
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("Unable to create client identity strategy '" + strategy + "' " +
+                            "class '" + identityResolverClass.getName() + "'", e);
+                }
+            }
+
+        }
+        return identityResolver;
+    }
+
+
+    public static void setIdentityResolver(IdentityResolver identityResolver) {
+        ClientSecurity.identityResolver = identityResolver;
+    }
+
+    private ClientSecurity() {
+    }
+
+    public static class SimpleIdentityResolver implements IdentityResolver {
+        public Object getIdentity() {
+            Object clientIdentity = threadClientIdentity.get();
+            if (clientIdentity == null) {
+                clientIdentity = staticClientIdentity;
+            }
+            return clientIdentity;
+        }
+    }
+}

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBHomeHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBHomeHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBHomeHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBHomeHandler.java Sun Mar 25 13:04:39 2007
@@ -157,7 +157,6 @@
     public Object homeMethod(Method method, Object[] args, Object proxy) throws Throwable {
         EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_HOME_METHOD);
 
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setMethodInstance(method);
@@ -187,7 +186,6 @@
     protected Object create(Method method, Object[] args, Object proxy) throws Throwable {
         EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_HOME_CREATE);
 
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setMethodInstance(method);

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBInvocationHandler.java Sun Mar 25 13:04:39 2007
@@ -21,9 +21,13 @@
 import java.rmi.NoSuchObjectException;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
+import java.security.AccessController;
 
 import org.apache.openejb.client.proxy.InvocationHandler;
 
+import javax.security.auth.Subject;
+
 public abstract class EJBInvocationHandler implements InvocationHandler, Serializable {
 
     protected static final Method EQUALS = getMethod(Object.class, "equals", null);
@@ -78,7 +82,17 @@
     protected abstract Object _invoke(Object proxy, Method method, Object[] args) throws Throwable;
 
     protected EJBResponse request(EJBRequest req) throws Exception {
+        req.setClientIdentity(getClientIdentity());
         return (EJBResponse) Client.request(req, new EJBResponse(), server);
+    }
+
+    protected Object getClientIdentity() {
+        Object identity = client.getClientIdentity();
+        if (identity != null) {
+            return identity;
+        }
+
+        return ClientSecurity.getIdentity();
     }
 
     protected void invalidateReference() {

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EJBObjectHandler.java Sun Mar 25 13:04:39 2007
@@ -194,7 +194,6 @@
 
         req.setMethodParameters(args);
         req.setMethodInstance(method);
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setPrimaryKey(primaryKey);

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBHomeHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBHomeHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBHomeHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBHomeHandler.java Sun Mar 25 13:04:39 2007
@@ -36,7 +36,6 @@
 
         req.setMethodParameters(args);
         req.setMethodInstance(method);
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setPrimaryKey(primaryKey);

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBObjectHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBObjectHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBObjectHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/EntityEJBObjectHandler.java Sun Mar 25 13:04:39 2007
@@ -58,7 +58,6 @@
 
         req.setMethodParameters(args);
         req.setMethodInstance(method);
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setPrimaryKey(primaryKey);

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/IdentityResolver.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/IdentityResolver.java?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/IdentityResolver.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/IdentityResolver.java Sun Mar 25 13:04:39 2007
@@ -0,0 +1,22 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.client;
+
+public interface IdentityResolver {
+    Object getIdentity();
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java Sun Mar 25 13:04:39 2007
@@ -0,0 +1,39 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.client;
+
+import javax.security.auth.Subject;
+import java.security.AccessController;
+import java.util.Set;
+
+public class JaasIdentityResolver implements IdentityResolver {
+    public Object getIdentity() {
+        Subject subject = Subject.getSubject(AccessController.getContext());
+        if (subject == null) {
+            return null;
+        }
+
+        Set<ClientIdentityPrincipal> identityPrincipals = subject.getPrincipals(ClientIdentityPrincipal.class);
+        if (identityPrincipals.isEmpty()) {
+            return null;
+        }
+
+        ClientIdentityPrincipal principal = identityPrincipals.iterator().next();
+        return principal.getClientIdentity();
+    }
+}

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ResourceFinder.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ResourceFinder.java?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ResourceFinder.java (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ResourceFinder.java Sun Mar 25 13:04:39 2007
@@ -0,0 +1,1070 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.client;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Note: This class is a copy of the xbean-finder ResourceFinder class.  Any changes should
+ * be make to the xbean-finder source first and then copied to this class.  Also, this class
+ * should be kept in sync with the xbean-finder code.
+ * @author David Blevins
+ */
+public class ResourceFinder {
+
+    private final URL[] urls;
+    private final String path;
+    private final ClassLoader classLoader;
+    private final List<String> resourcesNotLoaded = new ArrayList<String>();
+
+    public ResourceFinder(URL... urls) {
+        this(null, Thread.currentThread().getContextClassLoader(), urls);
+    }
+
+    public ResourceFinder(String path) {
+        this(path, Thread.currentThread().getContextClassLoader(), null);
+    }
+
+    public ResourceFinder(String path, URL... urls) {
+        this(path, Thread.currentThread().getContextClassLoader(), urls);
+    }
+
+    public ResourceFinder(String path, ClassLoader classLoader) {
+        this(path, classLoader, null);
+    }
+
+    public ResourceFinder(String path, ClassLoader classLoader, URL... urls) {
+        if (path == null){
+            path = "";
+        } else if (path.length() > 0 && !path.endsWith("/")) {
+            path += "/";
+        }
+        this.path = path;
+
+        if (classLoader == null) {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        }
+        this.classLoader = classLoader;
+
+        for (int i = 0; urls != null && i < urls.length; i++) {
+            URL url = urls[i];
+            if (url == null || isDirectory(url) || url.getProtocol().equals("jar")) {
+                continue;
+            }
+            try {
+                urls[i] = new URL("jar", "", -1, url.toString() + "!/");
+            } catch (MalformedURLException e) {
+            }
+        }
+        this.urls = (urls == null || urls.length == 0)? null : urls;
+    }
+
+    private static boolean isDirectory(URL url) {
+        String file = url.getFile();
+        return (file.length() > 0 && file.charAt(file.length() - 1) == '/');
+    }
+
+    /**
+     * Returns a list of resources that could not be loaded in the last invoked findAvailable* or
+     * mapAvailable* methods.
+     * <p/>
+     * The list will only contain entries of resources that match the requirements
+     * of the last invoked findAvailable* or mapAvailable* methods, but were unable to be
+     * loaded and included in their results.
+     * <p/>
+     * The list returned is unmodifiable and the results of this method will change
+     * after each invocation of a findAvailable* or mapAvailable* methods.
+     * <p/>
+     * This method is not thread safe.
+     */
+    public List<String> getResourcesNotLoaded() {
+        return Collections.unmodifiableList(resourcesNotLoaded);
+    }
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Find
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    public URL find(String uri) throws IOException {
+        String fullUri = path + uri;
+
+        URL resource = getResource(fullUri);
+        if (resource == null) {
+            throw new IOException("Could not find resource '" + fullUri + "'");
+        }
+
+        return resource;
+    }
+
+    public List<URL> findAll(String uri) throws IOException {
+        String fullUri = path + uri;
+
+        Enumeration<URL> resources = getResources(fullUri);
+        List<URL> list = new ArrayList();
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            list.add(url);
+        }
+        return list;
+    }
+
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Find String
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    /**
+     * Reads the contents of the URL as a {@link String}'s and returns it.
+     *
+     * @param uri
+     * @return a stringified content of a resource
+     * @throws IOException if a resource pointed out by the uri param could not be find
+     * @see ClassLoader#getResource(String)
+     */
+    public String findString(String uri) throws IOException {
+        String fullUri = path + uri;
+
+        URL resource = getResource(fullUri);
+        if (resource == null) {
+            throw new IOException("Could not find a resource in : " + fullUri);
+        }
+
+        return readContents(resource);
+    }
+
+    /**
+     * Reads the contents of the found URLs as a list of {@link String}'s and returns them.
+     *
+     * @param uri
+     * @return a list of the content of each resource URL found
+     * @throws IOException if any of the found URLs are unable to be read.
+     */
+    public List<String> findAllStrings(String uri) throws IOException {
+        String fulluri = path + uri;
+
+        List<String> strings = new ArrayList<String>();
+
+        Enumeration<URL> resources = getResources(fulluri);
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            String string = readContents(url);
+            strings.add(string);
+        }
+        return strings;
+    }
+
+    /**
+     * Reads the contents of the found URLs as a Strings and returns them.
+     * Individual URLs that cannot be read are skipped and added to the
+     * list of 'resourcesNotLoaded'
+     *
+     * @param uri
+     * @return a list of the content of each resource URL found
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public List<String> findAvailableStrings(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        String fulluri = path + uri;
+
+        List<String> strings = new ArrayList<String>();
+
+        Enumeration<URL> resources = getResources(fulluri);
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            try {
+                String string = readContents(url);
+                strings.add(string);
+            } catch (IOException notAvailable) {
+                resourcesNotLoaded.add(url.toExternalForm());
+            }
+        }
+        return strings;
+    }
+
+    /**
+     * Reads the contents of all non-directory URLs immediately under the specified
+     * location and returns them in a map keyed by the file name.
+     * <p/>
+     * Any URLs that cannot be read will cause an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/serializables/one
+     * META-INF/serializables/two
+     * META-INF/serializables/three
+     * META-INF/serializables/four/foo.txt
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAvailableStrings("serializables");
+     * map.contains("one");  // true
+     * map.contains("two");  // true
+     * map.contains("three");  // true
+     * map.contains("four");  // false
+     *
+     * @param uri
+     * @return a list of the content of each resource URL found
+     * @throws IOException if any of the urls cannot be read
+     */
+    public Map<String, String> mapAllStrings(String uri) throws IOException {
+        Map<String, String> strings = new HashMap<String, String>();
+        Map<String, URL> resourcesMap = getResourcesMap(uri);
+        for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String name = (String) entry.getKey();
+            URL url = (URL) entry.getValue();
+            String value = readContents(url);
+            strings.put(name, value);
+        }
+        return strings;
+    }
+
+    /**
+     * Reads the contents of all non-directory URLs immediately under the specified
+     * location and returns them in a map keyed by the file name.
+     * <p/>
+     * Individual URLs that cannot be read are skipped and added to the
+     * list of 'resourcesNotLoaded'
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/serializables/one
+     * META-INF/serializables/two      # not readable
+     * META-INF/serializables/three
+     * META-INF/serializables/four/foo.txt
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAvailableStrings("serializables");
+     * map.contains("one");  // true
+     * map.contains("two");  // false
+     * map.contains("three");  // true
+     * map.contains("four");  // false
+     *
+     * @param uri
+     * @return a list of the content of each resource URL found
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public Map<String, String> mapAvailableStrings(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        Map<String, String> strings = new HashMap<String, String>();
+        Map<String, URL> resourcesMap = getResourcesMap(uri);
+        for (Iterator iterator = resourcesMap.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String name = (String) entry.getKey();
+            URL url = (URL) entry.getValue();
+            try {
+                String value = readContents(url);
+                strings.put(name, value);
+            } catch (IOException notAvailable) {
+                resourcesNotLoaded.add(url.toExternalForm());
+            }
+        }
+        return strings;
+    }
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Find Class
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    /**
+     * Executes {@link #findString(String)} assuming the contents URL found is the name of
+     * a class that should be loaded and returned.
+     *
+     * @param uri
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public Class findClass(String uri) throws IOException, ClassNotFoundException {
+        String className = findString(uri);
+        return (Class) classLoader.loadClass(className);
+    }
+
+    /**
+     * Executes findAllStrings assuming the strings are
+     * the names of a classes that should be loaded and returned.
+     * <p/>
+     * Any URL or class that cannot be loaded will cause an exception to be thrown.
+     *
+     * @param uri
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public List<Class> findAllClasses(String uri) throws IOException, ClassNotFoundException {
+        List<Class> classes = new ArrayList<Class>();
+        List<String> strings = findAllStrings(uri);
+        for (String className : strings) {
+            Class clazz = classLoader.loadClass(className);
+            classes.add(clazz);
+        }
+        return classes;
+    }
+
+    /**
+     * Executes findAvailableStrings assuming the strings are
+     * the names of a classes that should be loaded and returned.
+     * <p/>
+     * Any class that cannot be loaded will be skipped and placed in the
+     * 'resourcesNotLoaded' collection.
+     *
+     * @param uri
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public List<Class> findAvailableClasses(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        List<Class> classes = new ArrayList<Class>();
+        List<String> strings = findAvailableStrings(uri);
+        for (String className : strings) {
+            try {
+                Class clazz = classLoader.loadClass(className);
+                classes.add(clazz);
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(className);
+            }
+        }
+        return classes;
+    }
+
+    /**
+     * Executes mapAllStrings assuming the value of each entry in the
+     * map is the name of a class that should be loaded.
+     * <p/>
+     * Any class that cannot be loaded will be cause an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/xmlparsers/xerces
+     * META-INF/xmlparsers/crimson
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAvailableStrings("xmlparsers");
+     * map.contains("xerces");  // true
+     * map.contains("crimson");  // true
+     * Class xercesClass = map.get("xerces");
+     * Class crimsonClass = map.get("crimson");
+     *
+     * @param uri
+     * @return
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public Map<String, Class> mapAllClasses(String uri) throws IOException, ClassNotFoundException {
+        Map<String, Class> classes = new HashMap<String, Class>();
+        Map<String, String> map = mapAllStrings(uri);
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            String className = (String) entry.getValue();
+            Class clazz = classLoader.loadClass(className);
+            classes.put(string, clazz);
+        }
+        return classes;
+    }
+
+    /**
+     * Executes mapAvailableStrings assuming the value of each entry in the
+     * map is the name of a class that should be loaded.
+     * <p/>
+     * Any class that cannot be loaded will be skipped and placed in the
+     * 'resourcesNotLoaded' collection.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/xmlparsers/xerces
+     * META-INF/xmlparsers/crimson
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAvailableStrings("xmlparsers");
+     * map.contains("xerces");  // true
+     * map.contains("crimson");  // true
+     * Class xercesClass = map.get("xerces");
+     * Class crimsonClass = map.get("crimson");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public Map<String, Class> mapAvailableClasses(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        Map<String, Class> classes = new HashMap<String, Class>();
+        Map<String, String> map = mapAvailableStrings(uri);
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            String className = (String) entry.getValue();
+            try {
+                Class clazz = classLoader.loadClass(className);
+                classes.put(string, clazz);
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(className);
+            }
+        }
+        return classes;
+    }
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Find Implementation
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    /**
+     * Assumes the class specified points to a file in the classpath that contains
+     * the name of a class that implements or is a subclass of the specfied class.
+     * <p/>
+     * Any class that cannot be loaded will be cause an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/java.io.InputStream    # contains the classname org.acme.AcmeInputStream
+     * META-INF/java.io.OutputStream
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Class clazz = finder.findImplementation(java.io.InputStream.class);
+     * clazz.getName();  // returns "org.acme.AcmeInputStream"
+     *
+     * @param interfase a superclass or interface
+     * @return
+     * @throws IOException            if the URL cannot be read
+     * @throws ClassNotFoundException if the class found is not loadable
+     * @throws ClassCastException     if the class found is not assignable to the specified superclass or interface
+     */
+    public Class findImplementation(Class interfase) throws IOException, ClassNotFoundException {
+        String className = findString(interfase.getName());
+        Class impl = classLoader.loadClass(className);
+        if (!interfase.isAssignableFrom(impl)) {
+            throw new ClassCastException("Class not of type: " + interfase.getName());
+        }
+        return impl;
+    }
+
+    /**
+     * Assumes the class specified points to a file in the classpath that contains
+     * the name of a class that implements or is a subclass of the specfied class.
+     * <p/>
+     * Any class that cannot be loaded or assigned to the specified interface will be cause
+     * an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/java.io.InputStream    # contains the classname org.acme.AcmeInputStream
+     * META-INF/java.io.InputStream    # contains the classname org.widget.NeatoInputStream
+     * META-INF/java.io.InputStream    # contains the classname com.foo.BarInputStream
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List classes = finder.findAllImplementations(java.io.InputStream.class);
+     * classes.contains("org.acme.AcmeInputStream");  // true
+     * classes.contains("org.widget.NeatoInputStream");  // true
+     * classes.contains("com.foo.BarInputStream");  // true
+     *
+     * @param interfase a superclass or interface
+     * @return
+     * @throws IOException            if the URL cannot be read
+     * @throws ClassNotFoundException if the class found is not loadable
+     * @throws ClassCastException     if the class found is not assignable to the specified superclass or interface
+     */
+    public List<Class> findAllImplementations(Class interfase) throws IOException, ClassNotFoundException {
+        List<Class> implementations = new ArrayList<Class>();
+        List<String> strings = findAllStrings(interfase.getName());
+        for (String className : strings) {
+            Class impl = classLoader.loadClass(className);
+            if (!interfase.isAssignableFrom(impl)) {
+                throw new ClassCastException("Class not of type: " + interfase.getName());
+            }
+            implementations.add(impl);
+        }
+        return implementations;
+    }
+
+    /**
+     * Assumes the class specified points to a file in the classpath that contains
+     * the name of a class that implements or is a subclass of the specfied class.
+     * <p/>
+     * Any class that cannot be loaded or are not assignable to the specified class will be
+     * skipped and placed in the 'resourcesNotLoaded' collection.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/java.io.InputStream    # contains the classname org.acme.AcmeInputStream
+     * META-INF/java.io.InputStream    # contains the classname org.widget.NeatoInputStream
+     * META-INF/java.io.InputStream    # contains the classname com.foo.BarInputStream
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List classes = finder.findAllImplementations(java.io.InputStream.class);
+     * classes.contains("org.acme.AcmeInputStream");  // true
+     * classes.contains("org.widget.NeatoInputStream");  // true
+     * classes.contains("com.foo.BarInputStream");  // true
+     *
+     * @param interfase a superclass or interface
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public List<Class> findAvailableImplementations(Class interfase) throws IOException {
+        resourcesNotLoaded.clear();
+        List<Class> implementations = new ArrayList<Class>();
+        List<String> strings = findAvailableStrings(interfase.getName());
+        for (String className : strings) {
+            try {
+                Class impl = classLoader.loadClass(className);
+                if (interfase.isAssignableFrom(impl)) {
+                    implementations.add(impl);
+                } else {
+                    resourcesNotLoaded.add(className);
+                }
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(className);
+            }
+        }
+        return implementations;
+    }
+
+    /**
+     * Assumes the class specified points to a directory in the classpath that holds files
+     * containing the name of a class that implements or is a subclass of the specfied class.
+     * <p/>
+     * Any class that cannot be loaded or assigned to the specified interface will be cause
+     * an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/java.net.URLStreamHandler/jar
+     * META-INF/java.net.URLStreamHandler/file
+     * META-INF/java.net.URLStreamHandler/http
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class);
+     * Class jarUrlHandler = map.get("jar");
+     * Class fileUrlHandler = map.get("file");
+     * Class httpUrlHandler = map.get("http");
+     *
+     * @param interfase a superclass or interface
+     * @return
+     * @throws IOException            if the URL cannot be read
+     * @throws ClassNotFoundException if the class found is not loadable
+     * @throws ClassCastException     if the class found is not assignable to the specified superclass or interface
+     */
+    public Map<String, Class> mapAllImplementations(Class interfase) throws IOException, ClassNotFoundException {
+        Map<String, Class> implementations = new HashMap<String, Class>();
+        Map<String, String> map = mapAllStrings(interfase.getName());
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            String className = (String) entry.getValue();
+            Class impl = classLoader.loadClass(className);
+            if (!interfase.isAssignableFrom(impl)) {
+                throw new ClassCastException("Class not of type: " + interfase.getName());
+            }
+            implementations.put(string, impl);
+        }
+        return implementations;
+    }
+
+    /**
+     * Assumes the class specified points to a directory in the classpath that holds files
+     * containing the name of a class that implements or is a subclass of the specfied class.
+     * <p/>
+     * Any class that cannot be loaded or are not assignable to the specified class will be
+     * skipped and placed in the 'resourcesNotLoaded' collection.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/java.net.URLStreamHandler/jar
+     * META-INF/java.net.URLStreamHandler/file
+     * META-INF/java.net.URLStreamHandler/http
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Map map = finder.mapAllImplementations(java.net.URLStreamHandler.class);
+     * Class jarUrlHandler = map.get("jar");
+     * Class fileUrlHandler = map.get("file");
+     * Class httpUrlHandler = map.get("http");
+     *
+     * @param interfase a superclass or interface
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public Map<String, Class> mapAvailableImplementations(Class interfase) throws IOException {
+        resourcesNotLoaded.clear();
+        Map<String, Class> implementations = new HashMap<String, Class>();
+        Map<String, String> map = mapAvailableStrings(interfase.getName());
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            String className = (String) entry.getValue();
+            try {
+                Class impl = classLoader.loadClass(className);
+                if (interfase.isAssignableFrom(impl)) {
+                    implementations.put(string, impl);
+                } else {
+                    resourcesNotLoaded.add(className);
+                }
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(className);
+            }
+        }
+        return implementations;
+    }
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Find Properties
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    /**
+     * Finds the corresponding resource and reads it in as a properties file
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/widget.properties
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * Properties widgetProps = finder.findProperties("widget.properties");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if the URL cannot be read or is not in properties file format
+     */
+    public Properties findProperties(String uri) throws IOException {
+        String fulluri = path + uri;
+
+        URL resource = getResource(fulluri);
+        if (resource == null) {
+            throw new IOException("Could not find command in : " + fulluri);
+        }
+
+        return loadProperties(resource);
+    }
+
+    /**
+     * Finds the corresponding resources and reads them in as a properties files
+     * <p/>
+     * Any URL that cannot be read in as a properties file will cause an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/app.properties
+     * META-INF/app.properties
+     * META-INF/app.properties
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List<Properties> appProps = finder.findAllProperties("app.properties");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if the URL cannot be read or is not in properties file format
+     */
+    public List<Properties> findAllProperties(String uri) throws IOException {
+        String fulluri = path + uri;
+
+        List<Properties> properties = new ArrayList<Properties>();
+
+        Enumeration<URL> resources = getResources(fulluri);
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            Properties props = loadProperties(url);
+            properties.add(props);
+        }
+        return properties;
+    }
+
+    /**
+     * Finds the corresponding resources and reads them in as a properties files
+     * <p/>
+     * Any URL that cannot be read in as a properties file will be added to the
+     * 'resourcesNotLoaded' collection.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/app.properties
+     * META-INF/app.properties
+     * META-INF/app.properties
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List<Properties> appProps = finder.findAvailableProperties("app.properties");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public List<Properties> findAvailableProperties(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        String fulluri = path + uri;
+
+        List<Properties> properties = new ArrayList<Properties>();
+
+        Enumeration<URL> resources = getResources(fulluri);
+        while (resources.hasMoreElements()) {
+            URL url = resources.nextElement();
+            try {
+                Properties props = loadProperties(url);
+                properties.add(props);
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(url.toExternalForm());
+            }
+        }
+        return properties;
+    }
+
+    /**
+     * Finds the corresponding resources and reads them in as a properties files
+     * <p/>
+     * Any URL that cannot be read in as a properties file will cause an exception to be thrown.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/jdbcDrivers/oracle.properties
+     * META-INF/jdbcDrivers/mysql.props
+     * META-INF/jdbcDrivers/derby
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List<Properties> driversList = finder.findAvailableProperties("jdbcDrivers");
+     * Properties oracleProps = driversList.get("oracle.properties");
+     * Properties mysqlProps = driversList.get("mysql.props");
+     * Properties derbyProps = driversList.get("derby");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if the URL cannot be read or is not in properties file format
+     */
+    public Map<String, Properties> mapAllProperties(String uri) throws IOException {
+        Map<String, Properties> propertiesMap = new HashMap<String, Properties>();
+        Map<String, URL> map = getResourcesMap(uri);
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            URL url = (URL) entry.getValue();
+            Properties properties = loadProperties(url);
+            propertiesMap.put(string, properties);
+        }
+        return propertiesMap;
+    }
+
+    /**
+     * Finds the corresponding resources and reads them in as a properties files
+     * <p/>
+     * Any URL that cannot be read in as a properties file will be added to the
+     * 'resourcesNotLoaded' collection.
+     * <p/>
+     * Example classpath:
+     * <p/>
+     * META-INF/jdbcDrivers/oracle.properties
+     * META-INF/jdbcDrivers/mysql.props
+     * META-INF/jdbcDrivers/derby
+     * <p/>
+     * ResourceFinder finder = new ResourceFinder("META-INF/");
+     * List<Properties> driversList = finder.findAvailableProperties("jdbcDrivers");
+     * Properties oracleProps = driversList.get("oracle.properties");
+     * Properties mysqlProps = driversList.get("mysql.props");
+     * Properties derbyProps = driversList.get("derby");
+     *
+     * @param uri
+     * @return
+     * @throws IOException if classLoader.getResources throws an exception
+     */
+    public Map<String, Properties> mapAvailableProperties(String uri) throws IOException {
+        resourcesNotLoaded.clear();
+        Map<String, Properties> propertiesMap = new HashMap<String, Properties>();
+        Map<String, URL> map = getResourcesMap(uri);
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String string = (String) entry.getKey();
+            URL url = (URL) entry.getValue();
+            try {
+                Properties properties = loadProperties(url);
+                propertiesMap.put(string, properties);
+            } catch (Exception notAvailable) {
+                resourcesNotLoaded.add(url.toExternalForm());
+            }
+        }
+        return propertiesMap;
+    }
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+    //
+    //   Map Resources
+    //
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+    public Map<String, URL> getResourcesMap(String uri) throws IOException {
+        String basePath = path + uri;
+
+        Map<String, URL> resources = new HashMap<String, URL>();
+        if (!basePath.endsWith("/")) {
+            basePath += "/";
+        }
+        Enumeration<URL> urls = getResources(basePath);
+
+        while (urls.hasMoreElements()) {
+            URL location = urls.nextElement();
+
+            try {
+                if (location.getProtocol().equals("jar")) {
+
+                    readJarEntries(location, basePath, resources);
+
+                } else if (location.getProtocol().equals("file")) {
+
+                    readDirectoryEntries(location, resources);
+
+                }
+            } catch (Exception e) {
+            }
+        }
+
+        return resources;
+    }
+
+    private static void readDirectoryEntries(URL location, Map<String, URL> resources) throws MalformedURLException {
+        File dir = new File(location.getPath());
+        if (dir.isDirectory()) {
+            File[] files = dir.listFiles();
+            for (File file : files) {
+                if (!file.isDirectory()) {
+                    String name = file.getName();
+                    URL url = file.toURL();
+                    resources.put(name, url);
+                }
+            }
+        }
+    }
+
+    private static void readJarEntries(URL location, String basePath, Map<String, URL> resources) throws IOException {
+        JarURLConnection conn = (JarURLConnection) location.openConnection();
+        JarFile jarfile = null;
+        jarfile = conn.getJarFile();
+
+        Enumeration<JarEntry> entries = jarfile.entries();
+        while (entries != null && entries.hasMoreElements()) {
+            JarEntry entry = entries.nextElement();
+            String name = entry.getName();
+
+            if (entry.isDirectory() || !name.startsWith(basePath) || name.length() == basePath.length()) {
+                continue;
+            }
+
+            name = name.substring(basePath.length());
+
+            if (name.contains("/")) {
+                continue;
+            }
+
+            URL resource = new URL(location, name);
+            resources.put(name, resource);
+        }
+    }
+
+    private Properties loadProperties(URL resource) throws IOException {
+        InputStream in = resource.openStream();
+
+        BufferedInputStream reader = null;
+        try {
+            reader = new BufferedInputStream(in);
+            Properties properties = new Properties();
+            properties.load(reader);
+
+            return properties;
+        } finally {
+            try {
+                in.close();
+                reader.close();
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    private String readContents(URL resource) throws IOException {
+        InputStream in = resource.openStream();
+        BufferedInputStream reader = null;
+        StringBuffer sb = new StringBuffer();
+
+        try {
+            reader = new BufferedInputStream(in);
+
+            int b = reader.read();
+            while (b != -1) {
+                sb.append((char) b);
+                b = reader.read();
+            }
+
+            return sb.toString().trim();
+        } finally {
+            try {
+                in.close();
+                reader.close();
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    private URL getResource(String fullUri) {
+        if (urls == null){
+            return classLoader.getResource(fullUri);
+        }
+        return findResource(fullUri, urls);
+    }
+
+    private Enumeration<URL> getResources(String fulluri) throws IOException {
+        if (urls == null) {
+            return classLoader.getResources(fulluri);
+        }
+        Vector<URL> resources = new Vector();
+        for (URL url : urls) {
+            URL resource = findResource(fulluri, url);
+            if (resource != null){
+                resources.add(resource);
+            }
+        }
+        return resources.elements();
+    }
+
+    private URL findResource(String resourceName, URL... search) {
+        for (int i = 0; i < search.length; i++) {
+            URL currentUrl = search[i];
+            if (currentUrl == null) {
+                continue;
+            }
+            JarFile jarFile = null;
+            try {
+                String protocol = currentUrl.getProtocol();
+                if (protocol.equals("jar")) {
+                    /*
+                    * If the connection for currentUrl or resURL is
+                    * used, getJarFile() will throw an exception if the
+                    * entry doesn't exist.
+                    */
+                    URL jarURL = ((JarURLConnection) currentUrl.openConnection()).getJarFileURL();
+                    try {
+                        JarURLConnection juc = (JarURLConnection) new URL("jar", "", jarURL.toExternalForm() + "!/").openConnection();
+                        jarFile = juc.getJarFile();
+                    } catch (IOException e) {
+                        // Don't look for this jar file again
+                        search[i] = null;
+                        throw e;
+                    }
+
+                    String entryName;
+                    if (currentUrl.getFile().endsWith("!/")) {
+                        entryName = resourceName;
+                    } else {
+                        String file = currentUrl.getFile();
+                        int sepIdx = file.lastIndexOf("!/");
+                        if (sepIdx == -1) {
+                            // Invalid URL, don't look here again
+                            search[i] = null;
+                            continue;
+                        }
+                        sepIdx += 2;
+                        StringBuffer sb = new StringBuffer(file.length() - sepIdx + resourceName.length());
+                        sb.append(file.substring(sepIdx));
+                        sb.append(resourceName);
+                        entryName = sb.toString();
+                    }
+                    if (entryName.equals("META-INF/") && jarFile.getEntry("META-INF/MANIFEST.MF") != null){
+                        return targetURL(currentUrl, "META-INF/MANIFEST.MF");
+                    }
+                    if (jarFile.getEntry(entryName) != null) {
+                        return targetURL(currentUrl, resourceName);
+                    }
+                } else if (protocol.equals("file")) {
+                    String baseFile = currentUrl.getFile();
+                    String host = currentUrl.getHost();
+                    int hostLength = 0;
+                    if (host != null) {
+                        hostLength = host.length();
+                    }
+                    StringBuffer buf = new StringBuffer(2 + hostLength + baseFile.length() + resourceName.length());
+
+                    if (hostLength > 0) {
+                        buf.append("//").append(host);
+                    }
+                    // baseFile always ends with '/'
+                    buf.append(baseFile);
+                    String fixedResName = resourceName;
+                    // Do not create a UNC path, i.e. \\host
+                    while (fixedResName.startsWith("/") || fixedResName.startsWith("\\")) {
+                        fixedResName = fixedResName.substring(1);
+                    }
+                    buf.append(fixedResName);
+                    String filename = buf.toString();
+                    File file = new File(filename);
+                    if (file.exists()) {
+                        return targetURL(currentUrl, fixedResName);
+                    }
+                } else {
+                    URL resourceURL = targetURL(currentUrl, resourceName);
+                    URLConnection urlConnection = resourceURL.openConnection();
+
+                    try {
+                        urlConnection.getInputStream().close();
+                    } catch (SecurityException e) {
+                        return null;
+                    }
+                    // HTTP can return a stream on a non-existent file
+                    // So check for the return code;
+                    if (!resourceURL.getProtocol().equals("http")) {
+                        return resourceURL;
+                    }
+
+                    int code = ((HttpURLConnection) urlConnection).getResponseCode();
+                    if (code >= 200 && code < 300) {
+                        return resourceURL;
+                    }
+                }
+            } catch (MalformedURLException e) {
+                // Keep iterating through the URL list
+            } catch (IOException e) {
+            } catch (SecurityException e) {
+            }
+        }
+        return null;
+    }
+
+    private URL targetURL(URL base, String name) throws MalformedURLException {
+        StringBuffer sb = new StringBuffer(base.getFile().length() + name.length());
+        sb.append(base.getFile());
+        sb.append(name);
+        String file = sb.toString();
+        return new URL(base.getProtocol(), base.getHost(), base.getPort(), file, null);
+    }
+}

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/ServerMetaData.java Sun Mar 25 13:04:39 2007
@@ -21,6 +21,7 @@
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.net.URI;
+import java.util.Arrays;
 
 public class ServerMetaData implements Externalizable {
 
@@ -45,5 +46,8 @@
         out.writeObject(locations);
     }
 
+    public String toString() {
+        return Arrays.toString(locations);
+    }
 }
 

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBHomeHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBHomeHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBHomeHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBHomeHandler.java Sun Mar 25 13:04:39 2007
@@ -51,7 +51,6 @@
         }
 
         EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_HOME_REMOVE_BY_HANDLE);
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(handler.ejb.deploymentCode);
         req.setDeploymentId(handler.ejb.deploymentID);
         req.setMethodInstance(method);

Modified: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBObjectHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBObjectHandler.java?view=diff&rev=522339&r1=522338&r2=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBObjectHandler.java (original)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/java/org/apache/openejb/client/StatefulEJBObjectHandler.java Sun Mar 25 13:04:39 2007
@@ -53,7 +53,6 @@
     protected Object remove(Method method, Object[] args, Object proxy) throws Throwable {
 
         EJBRequest req = new EJBRequest(RequestMethodConstants.EJB_OBJECT_REMOVE);
-        req.setClientIdentity(client.getClientIdentity());
         req.setDeploymentCode(ejb.deploymentCode);
         req.setDeploymentId(ejb.deploymentID);
         req.setMethodInstance(method);

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/jaas
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/jaas?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/jaas (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/jaas Sun Mar 25 13:04:39 2007
@@ -0,0 +1 @@
+org.apache.openejb.client.SubjectIdentityResolver
\ No newline at end of file

Added: incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/simple
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/simple?view=auto&rev=522339
==============================================================================
--- incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/simple (added)
+++ incubator/openejb/trunk/openejb3/server/openejb-client/src/main/resources/META-INF/org.apache.openejb.client.IdentityResolver/simple Sun Mar 25 13:04:39 2007
@@ -0,0 +1 @@
+org.apache.openejb.client.ClientSecurity$SimpleIdentityResolver
\ No newline at end of file