You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by an...@apache.org on 2015/07/01 22:39:26 UTC

[1/2] tomee git commit: First hack at logout

Repository: tomee
Updated Branches:
  refs/heads/tomee-1.7.x 809ed6bb5 -> ef7b52bdb


http://git-wip-us.apache.org/repos/asf/tomee/blob/ef7b52bd/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
----------------------------------------------------------------------
diff --git a/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java b/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
index ce3ff80..9b3b3b1 100644
--- a/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
+++ b/server/openejb-client/src/main/java/org/apache/openejb/client/JNDIContext.java
@@ -1,832 +1,861 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.openejb.client;
-
-import org.apache.openejb.client.event.RemoteInitialContextCreated;
-import org.apache.openejb.client.serializer.EJBDSerializer;
-import org.omg.CORBA.ORB;
-
-import javax.naming.AuthenticationException;
-import javax.naming.Binding;
-import javax.naming.CompoundName;
-import javax.naming.ConfigurationException;
-import javax.naming.Context;
-import javax.naming.InvalidNameException;
-import javax.naming.Name;
-import javax.naming.NameClassPair;
-import javax.naming.NameNotFoundException;
-import javax.naming.NameParser;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.OperationNotSupportedException;
-import javax.naming.Reference;
-import javax.naming.ServiceUnavailableException;
-import javax.naming.spi.InitialContextFactory;
-import javax.naming.spi.NamingManager;
-import javax.sql.DataSource;
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.net.ConnectException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.rmi.RemoteException;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * @version $Rev$ $Date$
- */
-@SuppressWarnings("UseOfObsoleteCollectionType")
-public class JNDIContext implements InitialContextFactory, Context {
-    private static final Logger LOGGER = Logger.getLogger("OpenEJB.client");
-
-    public static final String DEFAULT_PROVIDER_URL = "ejbd://localhost:4201";
-    public static final String SERIALIZER = "openejb.ejbd.serializer";
-    public static final String AUTHENTICATE_WITH_THE_REQUEST = "openejb.ejbd.authenticate-with-request";
-    public static final String POOL_QUEUE_SIZE = "openejb.client.invoker.queue";
-    public static final String POOL_THREAD_NUMBER = "openejb.client.invoker.threads";
-
-    private String tail = "/";
-    private ServerMetaData server;
-    private ClientMetaData client;
-    private Hashtable env;
-    private String moduleId;
-    private ClientInstance clientIdentity;
-
-    private static final ThreadPoolExecutor GLOBAL_CLIENT_POOL = newExecutor(10, null);
-
-    static {
-        final ClassLoader classLoader = Client.class.getClassLoader();
-        Class<?> container;
-        try {
-            container = Class.forName("org.apache.openejb.OpenEJB", false, classLoader);
-        } catch (final Throwable e) {
-            container = null;
-        }
-        if (classLoader == ClassLoader.getSystemClassLoader() || Boolean.getBoolean("openejb.client.flus-tasks")
-            || (container != null && container.getClassLoader() == classLoader)) {
-            Runtime.getRuntime().addShutdownHook(new Thread() {
-                @Override
-                public void run() {
-                    waitEndOfTasks(GLOBAL_CLIENT_POOL);
-                }
-            });
-        }
-    }
-
-    private AuthenticationInfo authenticationInfo = null;
-
-    //TODO figure out how to configure and manage the thread pool on the client side, this will do for now...
-    private transient int threads;
-    private transient LinkedBlockingQueue<Runnable> blockingQueue;
-
-    protected transient ThreadPoolExecutor executorService;
-
-    public static ThreadPoolExecutor globalExecutor() {
-        return GLOBAL_CLIENT_POOL;
-    }
-
-    private ThreadPoolExecutor executor() {
-        if (executorService != null) {
-            return executorService;
-        }
-        if (threads < 0) {
-            return GLOBAL_CLIENT_POOL;
-        }
-        synchronized (this) {
-            if (executorService != null) {
-                return executorService;
-            }
-            executorService = newExecutor(threads, blockingQueue);
-        }
-        return executorService;
-    }
-
-    public static ThreadPoolExecutor newExecutor(final int threads, final BlockingQueue<Runnable> blockingQueue) {
-        /**
-         This thread pool starts with 3 core threads and can grow to the limit defined by 'threads'.
-         If a pool thread is idle for more than 1 minute it will be discarded, unless the core size is reached.
-         It can accept upto the number of processes defined by 'queue'.
-         If the queue is full then an attempt is made to add the process to the queue for 10 seconds.
-         Failure to add to the queue in this time will either result in a logged rejection, or if 'block'
-         is true then a final attempt is made to run the process in the current thread (the service thread).
-         */
-
-        final ThreadPoolExecutor executorService = new ThreadPoolExecutor(3, (threads < 3 ? 3 : threads), 1, TimeUnit.MINUTES, blockingQueue == null ? new LinkedBlockingDeque<Runnable>(Integer.parseInt(getProperty(null, POOL_QUEUE_SIZE, "2"))) : blockingQueue);
-        executorService.setThreadFactory(new ThreadFactory() {
-
-            private final AtomicInteger i = new AtomicInteger(0);
-
-            @Override
-            public Thread newThread(final Runnable r) {
-                final Thread t = new Thread(r, "OpenEJB.Client." + i.incrementAndGet());
-                t.setDaemon(true);
-                t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-                    @Override
-                    public void uncaughtException(final Thread t, final Throwable e) {
-                        Logger.getLogger(EJBObjectHandler.class.getName()).log(Level.SEVERE, "Uncaught error in: " + t.getName(), e);
-                    }
-                });
-
-                return t;
-            }
-
-        });
-
-        executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
-            @Override
-            public void rejectedExecution(final Runnable r, final ThreadPoolExecutor tpe) {
-
-                if (null == r || null == tpe || tpe.isShutdown() || tpe.isTerminated() || tpe.isTerminating()) {
-                    return;
-                }
-
-                final Logger log = Logger.getLogger(EJBObjectHandler.class.getName());
-
-                if (log.isLoggable(Level.WARNING)) {
-                    log.log(Level.WARNING, "EJBObjectHandler ExecutorService at capicity for process: " + r);
-                }
-
-                boolean offer = false;
-                try {
-                    offer = tpe.getQueue().offer(r, 10, TimeUnit.SECONDS);
-                } catch (final InterruptedException e) {
-                    //Ignore
-                }
-
-                if (!offer) {
-                    log.log(Level.SEVERE, "EJBObjectHandler ExecutorService failed to run asynchronous process: " + r);
-                }
-            }
-        });
-        return executorService;
-    }
-
-    public JNDIContext() {
-    }
-
-    /*
-     * A neater version of clone
-     */
-    public JNDIContext(final JNDIContext that) {
-        this.tail = that.tail;
-        this.server = that.server;
-        this.client = that.client;
-        this.moduleId = that.moduleId;
-        this.env = (Hashtable) that.env.clone();
-        this.clientIdentity = that.clientIdentity;
-    }
-
-    private JNDIResponse request(final JNDIRequest req) throws Exception {
-        req.setServerHash(server.buildHash());
-
-        final JNDIResponse response = new JNDIResponse();
-        Client.request(req, response, server);
-        if (null != response.getServer()) {
-            server.merge(response.getServer());
-        }
-        return response;
-    }
-
-    protected AuthenticationResponse requestAuthorization(final AuthenticationRequest req) throws RemoteException {
-        return (AuthenticationResponse) Client.request(req, new AuthenticationResponse(), server);
-    }
-
-    @Override
-    public Context getInitialContext(final Hashtable environment) throws NamingException {
-        if (environment == null) {
-            throw new NamingException("Invalid argument, hashtable cannot be null.");
-        } else {
-            env = (Hashtable) environment.clone();
-        }
-
-        final String userID = (String) env.get(Context.SECURITY_PRINCIPAL);
-        final String psswrd = (String) env.get(Context.SECURITY_CREDENTIALS);
-        String providerUrl = (String) env.get(Context.PROVIDER_URL);
-        final String serializer = (String) env.get(SERIALIZER);
-        final boolean authWithRequest = "true".equalsIgnoreCase(String.class.cast(env.get(AUTHENTICATE_WITH_THE_REQUEST)));
-        moduleId = (String) env.get("openejb.client.moduleId");
-
-        final URI location;
-        try {
-            providerUrl = addMissingParts(providerUrl);
-            location = new URI(providerUrl);
-        } catch (final URISyntaxException e) {
-            throw (ConfigurationException) new ConfigurationException("Property value for " +
-                Context.PROVIDER_URL +
-                " invalid: " +
-                providerUrl +
-                " - " +
-                e.getMessage()).initCause(e);
-        }
-        this.server = new ServerMetaData(location);
-
-        final Client.Context context = Client.getContext(this.server);
-        context.getProperties().putAll(environment);
-
-        final String strategy = context.getOptions().get("openejb.client.connection.strategy", "default");
-        context.getClusterMetaData().setConnectionStrategy(strategy);
-
-        Client.fireEvent(new RemoteInitialContextCreated(location));
-
-        //TODO:1: Either aggressively initiate authentication or wait for the
-        //        server to send us an authentication challange.
-        if (userID != null) {
-            if (!authWithRequest) {
-                authenticate(userID, psswrd);
-            } else {
-                authenticationInfo = new AuthenticationInfo(String.class.cast(env.get("openejb.authentication.realmName")), userID, psswrd.toCharArray());
-            }
-        }
-        if (client == null) {
-            client = new ClientMetaData();
-        }
-
-        if (serializer != null) {
-            try {
-                client.setSerializer(EJBDSerializer.class.cast(Thread.currentThread().getContextClassLoader().loadClass(serializer).newInstance()));
-            } catch (final Exception e) {
-                // no-op
-            }
-        }
-
-        final int queue = Integer.parseInt(getProperty(env, JNDIContext.POOL_QUEUE_SIZE, "2"));
-        blockingQueue = new LinkedBlockingQueue<Runnable>((queue < 2 ? 2 : queue));
-        threads = Integer.parseInt(getProperty(env, "openejb.client.invoker.threads", "-1"));
-
-        return this;
-    }
-
-    private static String getProperty(final Hashtable env, final String key, final String defaultValue) {
-        final Object value = env == null ? null : env.get(key);
-        if (value != null) {
-            return value.toString();
-        }
-        return System.getProperty(key, defaultValue);
-    }
-
-    /**
-     * Add missing parts - expected only part of the required providerUrl
-     * <p/>
-     * TODO: Move the check to a place where it really belongs - ConnectionManager, ConnectionFactory or such
-     * This method (class in general) doesn't really know what is required as far as connection details go
-     * Assuming that java.net.URI or java.net.URL are going to be used is overly stated
-     */
-    String addMissingParts(String providerUrl) throws URISyntaxException {
-
-        final int port = Integer.parseInt(System.getProperty("ejbd.port", "4201"));
-
-        if (providerUrl == null || providerUrl.length() == 0) {
-            providerUrl = "ejbd://localhost:" + port;
-        } else {
-
-            final int colonIndex = providerUrl.indexOf(":");
-            final int slashesIndex = providerUrl.indexOf("//");
-
-            if (colonIndex == -1 && slashesIndex == -1) {   // hostname or ip address only
-                providerUrl = "ejbd://" + providerUrl + ":" + port;
-            } else if (colonIndex == -1) {
-                final URI providerUri = new URI(providerUrl);
-                final String scheme = providerUri.getScheme();
-                if (!(scheme.equals("http") || scheme.equals("https"))) {
-                    providerUrl = providerUrl + ":" + port;
-                }
-            } else if (slashesIndex == -1) {
-                providerUrl = "ejbd://" + providerUrl;
-            }
-        }
-        return providerUrl;
-    }
-
-    public void authenticate(final String userID, final String psswrd) throws AuthenticationException {
-
-        final AuthenticationRequest req = new AuthenticationRequest(String.class.cast(env.get("openejb.authentication.realmName")), userID, psswrd);
-
-        final AuthenticationResponse res;
-        try {
-            res = requestAuthorization(req);
-        } catch (final RemoteException e) {
-            throw new AuthenticationException(e.getLocalizedMessage());
-        }
-
-        switch (res.getResponseCode()) {
-            case ResponseCodes.AUTH_GRANTED:
-                client = res.getIdentity();
-                break;
-            case ResponseCodes.AUTH_REDIRECT:
-                client = res.getIdentity();
-                server = res.getServer();
-                break;
-            case ResponseCodes.AUTH_DENIED:
-                throw (AuthenticationException) new AuthenticationException("This principle is not authorized.").initCause(res.getDeniedCause());
-        }
-    }
-
-    public EJBHomeProxy createEJBHomeProxy(final EJBMetaDataImpl ejbData) {
-        final EJBHomeHandler handler = EJBHomeHandler.createEJBHomeHandler(executor(), ejbData, server, client, authenticationInfo);
-        final EJBHomeProxy proxy = handler.createEJBHomeProxy();
-        handler.ejb.ejbHomeProxy = proxy;
-
-        return proxy;
-
-    }
-
-    private Object createBusinessObject(final Object result) {
-        final EJBMetaDataImpl ejb = (EJBMetaDataImpl) result;
-        final Object primaryKey = ejb.getPrimaryKey();
-
-        final EJBObjectHandler handler = EJBObjectHandler.createEJBObjectHandler(executor(), ejb, server, client, primaryKey, authenticationInfo);
-        return handler.createEJBObjectProxy();
-    }
-
-    @Override
-    public Object lookup(String name) throws NamingException {
-
-        if (name == null) {
-            throw new InvalidNameException("The name cannot be null");
-        } else if (name.equals("")) {
-            return new JNDIContext(this);
-        } else if (name.startsWith("java:")) {
-            name = name.replaceFirst("^java:", "");
-        } else if (!name.startsWith("/")) {
-            name = tail + name;
-        }
-
-        final String prop = name.replaceFirst("comp/env/", "");
-        String value = System.getProperty(prop);
-        if (value != null) {
-            return parseEntry(prop, value);
-        }
-
-        if (name.equals("comp/ORB")) {
-            return getDefaultOrb();
-        }
-
-        final JNDIRequest req = new JNDIRequest();
-        req.setRequestMethod(RequestMethodCode.JNDI_LOOKUP);
-        req.setRequestString(name);
-        req.setModuleId(moduleId);
-
-        final JNDIResponse res;
-        try {
-            res = request(req);
-        } catch (Exception e) {
-            if (e instanceof RemoteException && e.getCause() instanceof ConnectException) {
-                e = (Exception) e.getCause();
-                throw (ServiceUnavailableException) new ServiceUnavailableException("Cannot lookup '" + name + "'.").initCause(e);
-            }
-            throw (NamingException) new NamingException("Cannot lookup '" + name + "'.").initCause(e);
-        }
-
-        switch (res.getResponseCode()) {
-            case ResponseCodes.JNDI_EJBHOME:
-                return createEJBHomeProxy((EJBMetaDataImpl) res.getResult());
-
-            case ResponseCodes.JNDI_BUSINESS_OBJECT:
-                return createBusinessObject(res.getResult());
-
-            case ResponseCodes.JNDI_OK:
-                return res.getResult();
-
-            case ResponseCodes.JNDI_INJECTIONS:
-                return res.getResult();
-
-            case ResponseCodes.JNDI_CONTEXT:
-                final JNDIContext subCtx = new JNDIContext(this);
-                if (!name.endsWith("/")) {
-                    name += '/';
-                }
-                subCtx.tail = name;
-                return subCtx;
-
-            case ResponseCodes.JNDI_DATA_SOURCE:
-                return createDataSource((DataSourceMetaData) res.getResult());
-
-            case ResponseCodes.JNDI_WEBSERVICE:
-                return createWebservice((WsMetaData) res.getResult());
-
-            case ResponseCodes.JNDI_RESOURCE:
-                final String type = (String) res.getResult();
-                value = System.getProperty("Resource/" + type);
-                if (value == null) {
-                    return null;
-                }
-                return parseEntry(prop, value);
-
-            case ResponseCodes.JNDI_REFERENCE:
-                final Reference ref = (Reference) res.getResult();
-                try {
-                    return NamingManager.getObjectInstance(ref, getNameParser(name).parse(name), this, env);
-                } catch (final Exception e) {
-                    throw (NamingException) new NamingException("Could not dereference " + ref).initCause(e);
-                }
-
-            case ResponseCodes.JNDI_NOT_FOUND:
-                throw new NameNotFoundException(name + " does not exist in the system.  Check that the app was successfully deployed.");
-
-            case ResponseCodes.JNDI_NAMING_EXCEPTION:
-                final Throwable throwable = ((ThrowableArtifact) res.getResult()).getThrowable();
-                if (throwable instanceof NamingException) {
-                    throw (NamingException) throwable;
-                }
-                throw (NamingException) new NamingException().initCause(throwable);
-
-            case ResponseCodes.JNDI_RUNTIME_EXCEPTION:
-                throw (RuntimeException) res.getResult();
-
-            case ResponseCodes.JNDI_ERROR:
-                throw (Error) res.getResult();
-
-            default:
-                throw new ClientRuntimeException("Invalid response from server: " + res.getResponseCode());
-        }
-    }
-
-    private Object parseEntry(final String name, String value) throws NamingException {
-        try {
-            URI uri = new URI(value);
-            final String scheme = uri.getScheme();
-            if (scheme.equals("link")) {
-                value = System.getProperty(uri.getSchemeSpecificPart());
-                if (value == null) {
-                    return null;
-                }
-                return parseEntry(name, value);
-            } else if (scheme.equals("datasource")) {
-                uri = new URI(uri.getSchemeSpecificPart());
-                final String driver = uri.getScheme();
-                final String url = uri.getSchemeSpecificPart();
-                return new ClientDataSource(driver, url, null, null);
-            } else if (scheme.equals("connectionfactory")) {
-                return build(uri);
-            } else if (scheme.equals("javamail")) {
-                return javax.mail.Session.getDefaultInstance(new Properties());
-            } else if (scheme.equals("orb")) {
-                return getDefaultOrb();
-            } else if (scheme.equals("queue")) {
-                return build(uri);
-            } else if (scheme.equals("topic")) {
-                return build(uri);
-            } else {
-                throw new UnsupportedOperationException("Unsupported Naming URI scheme '" + scheme + "'");
-            }
-        } catch (final URISyntaxException e) {
-            throw (NamingException) new NamingException("Unparsable jndi entry '" + name + "=" + value + "'.  Exception: " + e.getMessage()).initCause(e);
-        }
-    }
-
-    private Object build(final URI inputUri) throws URISyntaxException {
-        final URI uri = new URI(inputUri.getSchemeSpecificPart());
-        final String driver = uri.getScheme();
-        final String url = uri.getSchemeSpecificPart();
-        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-        if (classLoader == null) {
-            getClass().getClassLoader();
-        }
-        if (classLoader == null) {
-            ClassLoader.getSystemClassLoader();
-        }
-        try {
-            final Class<?> clazz = Class.forName(driver, true, classLoader);
-            final Constructor<?> constructor = clazz.getConstructor(String.class);
-            return constructor.newInstance(url);
-        } catch (final Exception e) {
-            throw new IllegalStateException("Cannot use " + driver + " with parameter " + url, e);
-        }
-    }
-
-    private DataSource createDataSource(final DataSourceMetaData dataSourceMetaData) {
-        return new ClientDataSource(dataSourceMetaData);
-    }
-
-    private Object createWebservice(final WsMetaData webserviceMetaData) throws NamingException {
-        try {
-            return webserviceMetaData.createWebservice();
-        } catch (final Exception e) {
-            throw (NamingException) new NamingException("Error creating webservice").initCause(e);
-        }
-    }
-
-    private ORB getDefaultOrb() {
-        return ORB.init();
-    }
-
-    @Override
-    public Object lookup(final Name name) throws NamingException {
-        return lookup(name.toString());
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
-        if (name == null) {
-            throw new InvalidNameException("The name cannot be null");
-        } else if (name.startsWith("java:")) {
-            name = name.replaceFirst("^java:", "");
-        } else if (!name.startsWith("/")) {
-            name = tail + name;
-        }
-
-        final JNDIRequest req = new JNDIRequest(RequestMethodCode.JNDI_LIST, name);
-        req.setModuleId(moduleId);
-
-        final JNDIResponse res;
-        try {
-            res = request(req);
-        } catch (Exception e) {
-            if (e instanceof RemoteException && e.getCause() instanceof ConnectException) {
-                e = (Exception) e.getCause();
-                throw (ServiceUnavailableException) new ServiceUnavailableException("Cannot list '" + name + "'.").initCause(e);
-            }
-            throw (NamingException) new NamingException("Cannot list '" + name + "'.").initCause(e);
-        }
-
-        switch (res.getResponseCode()) {
-
-            case ResponseCodes.JNDI_OK:
-                return null;
-
-            case ResponseCodes.JNDI_ENUMERATION:
-                return (NamingEnumeration) res.getResult();
-
-            case ResponseCodes.JNDI_NOT_FOUND:
-                throw new NameNotFoundException(name);
-
-            case ResponseCodes.JNDI_NAMING_EXCEPTION:
-                final Throwable throwable = ((ThrowableArtifact) res.getResult()).getThrowable();
-                if (throwable instanceof NamingException) {
-                    throw (NamingException) throwable;
-                }
-                throw (NamingException) new NamingException().initCause(throwable);
-
-            case ResponseCodes.JNDI_ERROR:
-                throw (Error) res.getResult();
-
-            default:
-                throw new ClientRuntimeException("Invalid response from server :" + res.getResponseCode());
-        }
-
-    }
-
-    @Override
-    public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException {
-        return list(name.toString());
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public NamingEnumeration<Binding> listBindings(final String name) throws NamingException {
-        final Object o = lookup(name);
-        if (o instanceof Context) {
-            final Context context = (Context) o;
-            final NamingEnumeration<NameClassPair> enumeration = context.list("");
-            final List<NameClassPair> bindings = new ArrayList<NameClassPair>();
-
-            while (enumeration.hasMoreElements()) {
-                final NameClassPair pair = enumeration.nextElement();
-                bindings.add(new LazyBinding(pair.getName(), pair.getClassName(), context));
-            }
-
-            return new NameClassPairEnumeration(bindings);
-
-        } else {
-            return null;
-        }
-
-    }
-
-    private static class LazyBinding extends Binding {
-
-        private static final long serialVersionUID = 1L;
-        private RuntimeException failed;
-        private Context context;
-
-        public LazyBinding(final String name, final String className, final Context context) {
-            super(name, className, null);
-            this.context = context;
-        }
-
-        @Override
-        public synchronized Object getObject() {
-            if (super.getObject() == null) {
-                if (failed != null) {
-                    throw failed;
-                }
-                try {
-                    super.setObject(context.lookup(getName()));
-                } catch (final NamingException e) {
-                    throw failed = new ClientRuntimeException("Failed to lazily fetch the binding '" + getName() + "'", e);
-                }
-            }
-            return super.getObject();
-        }
-    }
-
-    @Override
-    public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException {
-        return listBindings(name.toString());
-    }
-
-    @Override
-    public Object lookupLink(final String name) throws NamingException {
-        return lookup(name);
-    }
-
-    @Override
-    public Object lookupLink(final Name name) throws NamingException {
-        return lookupLink(name.toString());
-    }
-
-    @Override
-    public NameParser getNameParser(final String name) throws NamingException {
-        return new SimpleNameParser();
-    }
-
-    @Override
-    public NameParser getNameParser(final Name name) throws NamingException {
-        return new SimpleNameParser();
-    }
-
-    @Override
-    public String composeName(final String name, final String prefix) throws NamingException {
-        throw new OperationNotSupportedException("TODO: Needs to be implemented");
-    }
-
-    @Override
-    public Name composeName(final Name name, final Name prefix) throws NamingException {
-        throw new OperationNotSupportedException("TODO: Needs to be implemented");
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public Object addToEnvironment(final String key, final Object value) throws NamingException {
-        return env.put(key, value);
-    }
-
-    @Override
-    public Object removeFromEnvironment(final String key) throws NamingException {
-        return env.remove(key);
-    }
-
-    @Override
-    public Hashtable getEnvironment() throws NamingException {
-        return (Hashtable) env.clone();
-    }
-
-    @Override
-    public String getNameInNamespace() throws NamingException {
-        return "";
-    }
-
-    @Override
-    public void close() throws NamingException {
-        waitEndOfTasks(executorService);
-    }
-
-    private static void waitEndOfTasks(final ExecutorService executor) {
-        if (executor == null) {
-            return;
-        }
-
-        final List<Runnable> runnables = executor.shutdownNow();
-        for (final Runnable r : runnables) {
-            try {
-                r.run();
-            } catch (final Throwable th) {
-                LOGGER.log(Level.SEVERE, th.getMessage(), th);
-            }
-        }
-    }
-
-    @Override
-    public void bind(final String name, final Object obj) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public void bind(final Name name, final Object obj) throws NamingException {
-        bind(name.toString(), obj);
-    }
-
-    @Override
-    public void rebind(final String name, final Object obj) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public void rebind(final Name name, final Object obj) throws NamingException {
-        rebind(name.toString(), obj);
-    }
-
-    @Override
-    public void unbind(final String name) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public void unbind(final Name name) throws NamingException {
-        unbind(name.toString());
-    }
-
-    @Override
-    public void rename(final String oldname, final String newname) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public void rename(final Name oldname, final Name newname) throws NamingException {
-        rename(oldname.toString(), newname.toString());
-    }
-
-    @Override
-    public void destroySubcontext(final String name) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public void destroySubcontext(final Name name) throws NamingException {
-        destroySubcontext(name.toString());
-    }
-
-    @Override
-    public Context createSubcontext(final String name) throws NamingException {
-        throw new OperationNotSupportedException();
-    }
-
-    @Override
-    public Context createSubcontext(final Name name) throws NamingException {
-        return createSubcontext(name.toString());
-    }
-
-    private static final class SimpleNameParser implements NameParser {
-
-        private static final Properties PARSER_PROPERTIES = new Properties();
-
-        static {
-            PARSER_PROPERTIES.put("jndi.syntax.direction", "left_to_right");
-            PARSER_PROPERTIES.put("jndi.syntax.separator", "/");
-        }
-
-        private SimpleNameParser() {
-        }
-
-        @Override
-        public Name parse(final String name) throws NamingException {
-            return new CompoundName(name, PARSER_PROPERTIES);
-        }
-    }
-
-    public static class AuthenticationInfo implements Serializable {
-
-        private static final long serialVersionUID = -8898613532355280735L;
-        private String realm;
-        private String user;
-        private char[] password;
-
-        public AuthenticationInfo(final String realm, final String user, final char[] password) {
-            this.realm = realm;
-            this.user = user;
-            this.password = password;
-        }
-
-        public String getRealm() {
-            return realm;
-        }
-
-        public String getUser() {
-            return user;
-        }
-
-        public char[] getPassword() {
-            return password;
-        }
-    }
-}
-
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.client;
+
+import org.apache.openejb.client.event.RemoteInitialContextCreated;
+import org.apache.openejb.client.serializer.EJBDSerializer;
+import org.omg.CORBA.ORB;
+
+import javax.naming.AuthenticationException;
+import javax.naming.Binding;
+import javax.naming.CompoundName;
+import javax.naming.ConfigurationException;
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameNotFoundException;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.Reference;
+import javax.naming.ServiceUnavailableException;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.NamingManager;
+import javax.sql.DataSource;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.net.ConnectException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class JNDIContext implements InitialContextFactory, Context {
+    private static final Logger LOGGER = Logger.getLogger("OpenEJB.client");
+
+    @SuppressWarnings("UnusedDeclaration")
+    public static final String DEFAULT_PROVIDER_URL = "ejbd://localhost:4201";
+    public static final String SERIALIZER = "openejb.ejbd.serializer";
+    public static final String AUTHENTICATE_WITH_THE_REQUEST = "openejb.ejbd.authenticate-with-request";
+    public static final String POOL_QUEUE_SIZE = "openejb.client.invoker.queue";
+    @SuppressWarnings("UnusedDeclaration")
+    public static final String POOL_THREAD_NUMBER = "openejb.client.invoker.threads";
+    public static final String AUTHENTICATION_REALM_NAME = "openejb.authentication.realmName";
+    public static final String IDENTITY_TIMEOUT = "tomee.authentication.identity.timeout";
+
+    private String tail = "/";
+    private ServerMetaData server;
+    private ClientMetaData client;
+    private Hashtable env;
+    private String moduleId;
+    private ClientInstance clientIdentity;
+    private boolean authWithRequest = false;
+
+    private static final ThreadPoolExecutor GLOBAL_CLIENT_POOL = newExecutor(10, null);
+
+    static {
+        final ClassLoader classLoader = Client.class.getClassLoader();
+        Class<?> container;
+        try {
+            container = Class.forName("org.apache.openejb.OpenEJB", false, classLoader);
+        } catch (final Throwable e) {
+            container = null;
+        }
+        if (classLoader == ClassLoader.getSystemClassLoader() || Boolean.getBoolean("openejb.client.flus-tasks")
+                || (container != null && container.getClassLoader() == classLoader)) {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    waitEndOfTasks(GLOBAL_CLIENT_POOL);
+                }
+            });
+        }
+    }
+
+    private AuthenticationInfo authenticationInfo = null;
+
+    //TODO figure out how to configure and manage the thread pool on the client side, this will do for now...
+    private transient int threads;
+    private transient LinkedBlockingQueue<Runnable> blockingQueue;
+
+    protected transient ThreadPoolExecutor executorService;
+
+    public static ThreadPoolExecutor globalExecutor() {
+        return GLOBAL_CLIENT_POOL;
+    }
+
+    private ThreadPoolExecutor executor() {
+        if (executorService != null) {
+            return executorService;
+        }
+        if (threads < 0) {
+            return GLOBAL_CLIENT_POOL;
+        }
+        synchronized (this) {
+            if (executorService != null) {
+                return executorService;
+            }
+            executorService = newExecutor(threads, blockingQueue);
+        }
+        return executorService;
+    }
+
+    public static ThreadPoolExecutor newExecutor(final int threads, final BlockingQueue<Runnable> blockingQueue) {
+        /**
+         This thread pool starts with 3 core threads and can grow to the limit defined by 'threads'.
+         If a pool thread is idle for more than 1 minute it will be discarded, unless the core size is reached.
+         It can accept up to the number of processes defined by 'queue'.
+         If the queue is full then an attempt is made to add the process to the queue for 10 seconds.
+         Failure to add to the queue in this time will either result in a logged rejection, or if 'block'
+         is true then a final attempt is made to run the process in the current thread (the service thread).
+         */
+
+        final ThreadPoolExecutor executorService = new ThreadPoolExecutor(3, (threads < 3 ? 3 : threads), 1, TimeUnit.MINUTES, blockingQueue == null ? new LinkedBlockingDeque<Runnable>(Integer.parseInt(getProperty(null, POOL_QUEUE_SIZE, "2"))) : blockingQueue);
+        executorService.setThreadFactory(new ThreadFactory() {
+
+            private final AtomicInteger i = new AtomicInteger(0);
+
+            @Override
+            public Thread newThread(final Runnable r) {
+                final Thread t = new Thread(r, "OpenEJB.Client." + i.incrementAndGet());
+                t.setDaemon(true);
+                t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+                    @Override
+                    public void uncaughtException(final Thread t, final Throwable e) {
+                        Logger.getLogger(EJBObjectHandler.class.getName()).log(Level.SEVERE, "Uncaught error in: " + t.getName(), e);
+                    }
+                });
+
+                return t;
+            }
+
+        });
+
+        executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
+            @Override
+            public void rejectedExecution(final Runnable r, final ThreadPoolExecutor tpe) {
+
+                if (null == r || null == tpe || tpe.isShutdown() || tpe.isTerminated() || tpe.isTerminating()) {
+                    return;
+                }
+
+                final Logger log = Logger.getLogger(EJBObjectHandler.class.getName());
+
+                if (log.isLoggable(Level.WARNING)) {
+                    log.log(Level.WARNING, "EJBObjectHandler ExecutorService at capacity for process: " + r);
+                }
+
+                boolean offer = false;
+                try {
+                    offer = tpe.getQueue().offer(r, 10, TimeUnit.SECONDS);
+                } catch (final InterruptedException e) {
+                    //Ignore
+                }
+
+                if (!offer) {
+                    log.log(Level.SEVERE, "EJBObjectHandler ExecutorService failed to run asynchronous process: " + r);
+                }
+            }
+        });
+        return executorService;
+    }
+
+    public JNDIContext() {
+    }
+
+    /*
+     * A neater version of clone
+     */
+    public JNDIContext(final JNDIContext that) {
+        this.tail = that.tail;
+        this.server = that.server;
+        this.client = that.client;
+        this.moduleId = that.moduleId;
+        this.env = (Hashtable) that.env.clone();
+        this.clientIdentity = that.clientIdentity;
+    }
+
+    private JNDIResponse request(final JNDIRequest req) throws Exception {
+        req.setServerHash(server.buildHash());
+
+        final JNDIResponse response = new JNDIResponse();
+        Client.request(req, response, server);
+        if (null != response.getServer()) {
+            server.merge(response.getServer());
+        }
+        return response;
+    }
+
+    protected AuthenticationResponse requestAuthorization(final AuthenticationRequest req) throws RemoteException {
+        return (AuthenticationResponse) Client.request(req, new AuthenticationResponse(), server);
+    }
+
+    @Override
+    public Context getInitialContext(final Hashtable environment) throws NamingException {
+        if (environment == null) {
+            throw new NamingException("Invalid argument, hashtable cannot be null.");
+        } else {
+            env = (Hashtable) environment.clone();
+        }
+
+        final String userID = (String) env.get(Context.SECURITY_PRINCIPAL);
+        final String psswrd = (String) env.get(Context.SECURITY_CREDENTIALS);
+        String providerUrl = (String) env.get(Context.PROVIDER_URL);
+        authWithRequest = "true".equalsIgnoreCase(String.class.cast(env.get(AUTHENTICATE_WITH_THE_REQUEST)));
+        moduleId = (String) env.get("openejb.client.moduleId");
+
+        final URI location;
+        try {
+            providerUrl = addMissingParts(providerUrl);
+            location = new URI(providerUrl);
+        } catch (final URISyntaxException e) {
+            throw (ConfigurationException) new ConfigurationException("Property value for " +
+                    Context.PROVIDER_URL +
+                    " invalid: " +
+                    providerUrl +
+                    " - " +
+                    e.getMessage()).initCause(e);
+        }
+        this.server = new ServerMetaData(location);
+
+        final Client.Context context = Client.getContext(this.server);
+        context.getProperties().putAll(environment);
+
+        final String strategy = context.getOptions().get("openejb.client.connection.strategy", "default");
+        context.getClusterMetaData().setConnectionStrategy(strategy);
+
+        Client.fireEvent(new RemoteInitialContextCreated(location));
+
+        //TODO: Either aggressively initiate authentication or wait for the server to send us an authentication challenge.
+        if (userID != null) {
+            if (!authWithRequest) {
+                authenticate(userID, psswrd, false);
+            } else {
+                authenticationInfo = new AuthenticationInfo(String.class.cast(env.get(AUTHENTICATION_REALM_NAME)), userID, psswrd.toCharArray(), getTimeout(env));
+            }
+        }
+        if (client == null) {
+            client = new ClientMetaData();
+        }
+
+        seedClientSerializer();
+
+        final int queue = Integer.parseInt(getProperty(env, JNDIContext.POOL_QUEUE_SIZE, "2"));
+        blockingQueue = new LinkedBlockingQueue<Runnable>((queue < 2 ? 2 : queue));
+        threads = Integer.parseInt(getProperty(env, "openejb.client.invoker.threads", "-1"));
+
+        return this;
+    }
+
+    private void seedClientSerializer() {
+        final String serializer = (String) env.get(SERIALIZER);
+        if (serializer != null) {
+            try {
+                client.setSerializer(EJBDSerializer.class.cast(Thread.currentThread().getContextClassLoader().loadClass(serializer).newInstance()));
+            } catch (final Exception e) {
+                // no-op
+            }
+        }
+    }
+
+    private long getTimeout(final Hashtable env) {
+        final Object o = env.get(IDENTITY_TIMEOUT);
+        if (null != o) {
+
+        }
+
+        return 0;
+    }
+
+    private static String getProperty(final Hashtable env, final String key, final String defaultValue) {
+        final Object value = env == null ? null : env.get(key);
+        if (value != null) {
+            return value.toString();
+        }
+        return System.getProperty(key, defaultValue);
+    }
+
+    /**
+     * Add missing parts - expected only part of the required providerUrl
+     * <p/>
+     * TODO: Move the check to a place where it really belongs - ConnectionManager, ConnectionFactory or such
+     * This method (class in general) doesn't really know what is required as far as connection details go
+     * Assuming that java.net.URI or java.net.URL are going to be used is overly stated
+     */
+    String addMissingParts(String providerUrl) throws URISyntaxException {
+
+        final int port = Integer.parseInt(System.getProperty("ejbd.port", "4201"));
+
+        if (providerUrl == null || providerUrl.length() == 0) {
+            providerUrl = "ejbd://localhost:" + port;
+        } else {
+
+            final int colonIndex = providerUrl.indexOf(":");
+            final int slashesIndex = providerUrl.indexOf("//");
+
+            if (colonIndex == -1 && slashesIndex == -1) {   // hostname or ip address only
+                providerUrl = "ejbd://" + providerUrl + ":" + port;
+            } else if (colonIndex == -1) {
+                final URI providerUri = new URI(providerUrl);
+                final String scheme = providerUri.getScheme();
+                if (!(scheme.equals("http") || scheme.equals("https"))) {
+                    providerUrl = providerUrl + ":" + port;
+                }
+            } else if (slashesIndex == -1) {
+                providerUrl = "ejbd://" + providerUrl;
+            }
+        }
+        return providerUrl;
+    }
+
+    public void authenticate(final String userID, final String psswrd, final boolean logout) throws AuthenticationException {
+
+        final AuthenticationRequest req = new AuthenticationRequest(String.class.cast(env.get(AUTHENTICATION_REALM_NAME)), userID, psswrd, getTimeout(env));
+        req.setLogout(logout);
+
+        final AuthenticationResponse res;
+        try {
+            res = requestAuthorization(req);
+        } catch (final RemoteException e) {
+            throw new AuthenticationException(e.getLocalizedMessage());
+        }
+
+        switch (res.getResponseCode()) {
+            case ResponseCodes.AUTH_GRANTED:
+                client = logout ? new ClientMetaData() : res.getIdentity();
+                break;
+            case ResponseCodes.AUTH_REDIRECT:
+                client = logout ? new ClientMetaData() : res.getIdentity();
+                server = res.getServer();
+                break;
+            case ResponseCodes.AUTH_DENIED:
+                throw (AuthenticationException) new AuthenticationException("This principle is not authorized.").initCause(res.getDeniedCause());
+        }
+
+        seedClientSerializer();
+    }
+
+    public EJBHomeProxy createEJBHomeProxy(final EJBMetaDataImpl ejbData) {
+        final EJBHomeHandler handler = EJBHomeHandler.createEJBHomeHandler(executor(), ejbData, server, client, authenticationInfo);
+        final EJBHomeProxy proxy = handler.createEJBHomeProxy();
+        handler.ejb.ejbHomeProxy = proxy;
+
+        return proxy;
+
+    }
+
+    private Object createBusinessObject(final Object result) {
+        final EJBMetaDataImpl ejb = (EJBMetaDataImpl) result;
+        final Object primaryKey = ejb.getPrimaryKey();
+
+        final EJBObjectHandler handler = EJBObjectHandler.createEJBObjectHandler(executor(), ejb, server, client, primaryKey, authenticationInfo);
+        return handler.createEJBObjectProxy();
+    }
+
+    @Override
+    public Object lookup(String name) throws NamingException {
+
+        if (name == null) {
+            throw new InvalidNameException("The name cannot be null");
+        } else if (name.equals("")) {
+            return new JNDIContext(this);
+        } else if (name.startsWith("java:")) {
+            name = name.replaceFirst("^java:", "");
+        } else if (!name.startsWith("/")) {
+            name = tail + name;
+        }
+
+        final String prop = name.replaceFirst("comp/env/", "");
+        String value = System.getProperty(prop);
+        if (value != null) {
+            return parseEntry(prop, value);
+        }
+
+        if (name.equals("comp/ORB")) {
+            return getDefaultOrb();
+        }
+
+        final JNDIRequest req = new JNDIRequest();
+        req.setRequestMethod(RequestMethodCode.JNDI_LOOKUP);
+        req.setRequestString(name);
+        req.setModuleId(moduleId);
+
+        final JNDIResponse res;
+        try {
+            res = request(req);
+        } catch (Exception e) {
+            if (e instanceof RemoteException && e.getCause() instanceof ConnectException) {
+                e = (Exception) e.getCause();
+                throw (ServiceUnavailableException) new ServiceUnavailableException("Cannot lookup '" + name + "'.").initCause(e);
+            }
+            throw (NamingException) new NamingException("Cannot lookup '" + name + "'.").initCause(e);
+        }
+
+        switch (res.getResponseCode()) {
+            case ResponseCodes.JNDI_EJBHOME:
+                return createEJBHomeProxy((EJBMetaDataImpl) res.getResult());
+
+            case ResponseCodes.JNDI_BUSINESS_OBJECT:
+                return createBusinessObject(res.getResult());
+
+            case ResponseCodes.JNDI_OK:
+                return res.getResult();
+
+            case ResponseCodes.JNDI_INJECTIONS:
+                return res.getResult();
+
+            case ResponseCodes.JNDI_CONTEXT:
+                final JNDIContext subCtx = new JNDIContext(this);
+                if (!name.endsWith("/")) {
+                    name += '/';
+                }
+                subCtx.tail = name;
+                return subCtx;
+
+            case ResponseCodes.JNDI_DATA_SOURCE:
+                return createDataSource((DataSourceMetaData) res.getResult());
+
+            case ResponseCodes.JNDI_WEBSERVICE:
+                return createWebservice((WsMetaData) res.getResult());
+
+            case ResponseCodes.JNDI_RESOURCE:
+                final String type = (String) res.getResult();
+                value = System.getProperty("Resource/" + type);
+                if (value == null) {
+                    return null;
+                }
+                return parseEntry(prop, value);
+
+            case ResponseCodes.JNDI_REFERENCE:
+                final Reference ref = (Reference) res.getResult();
+                try {
+                    return NamingManager.getObjectInstance(ref, getNameParser(name).parse(name), this, env);
+                } catch (final Exception e) {
+                    throw (NamingException) new NamingException("Could not dereference " + ref).initCause(e);
+                }
+
+            case ResponseCodes.JNDI_NOT_FOUND:
+                throw new NameNotFoundException(name + " does not exist in the system.  Check that the app was successfully deployed.");
+
+            case ResponseCodes.JNDI_NAMING_EXCEPTION:
+                final Throwable throwable = ((ThrowableArtifact) res.getResult()).getThrowable();
+                if (throwable instanceof NamingException) {
+                    throw (NamingException) throwable;
+                }
+                throw (NamingException) new NamingException().initCause(throwable);
+
+            case ResponseCodes.JNDI_RUNTIME_EXCEPTION:
+                throw (RuntimeException) res.getResult();
+
+            case ResponseCodes.JNDI_ERROR:
+                throw (Error) res.getResult();
+
+            default:
+                throw new ClientRuntimeException("Invalid response from server: " + res.getResponseCode());
+        }
+    }
+
+    private Object parseEntry(final String name, String value) throws NamingException {
+        try {
+            URI uri = new URI(value);
+            final String scheme = uri.getScheme();
+            if (scheme.equals("link")) {
+                value = System.getProperty(uri.getSchemeSpecificPart());
+                if (value == null) {
+                    return null;
+                }
+                return parseEntry(name, value);
+            } else if (scheme.equals("datasource")) {
+                uri = new URI(uri.getSchemeSpecificPart());
+                final String driver = uri.getScheme();
+                final String url = uri.getSchemeSpecificPart();
+                return new ClientDataSource(driver, url, null, null);
+            } else if (scheme.equals("connectionfactory")) {
+                return build(uri);
+            } else if (scheme.equals("javamail")) {
+                return javax.mail.Session.getDefaultInstance(new Properties());
+            } else if (scheme.equals("orb")) {
+                return getDefaultOrb();
+            } else if (scheme.equals("queue")) {
+                return build(uri);
+            } else if (scheme.equals("topic")) {
+                return build(uri);
+            } else {
+                throw new UnsupportedOperationException("Unsupported Naming URI scheme '" + scheme + "'");
+            }
+        } catch (final URISyntaxException e) {
+            throw (NamingException) new NamingException("Unparsable jndi entry '" + name + "=" + value + "'.  Exception: " + e.getMessage()).initCause(e);
+        }
+    }
+
+    private Object build(final URI inputUri) throws URISyntaxException {
+        final URI uri = new URI(inputUri.getSchemeSpecificPart());
+        final String driver = uri.getScheme();
+        final String url = uri.getSchemeSpecificPart();
+        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            getClass().getClassLoader();
+        }
+        if (classLoader == null) {
+            ClassLoader.getSystemClassLoader();
+        }
+        try {
+            final Class<?> clazz = Class.forName(driver, true, classLoader);
+            final Constructor<?> constructor = clazz.getConstructor(String.class);
+            return constructor.newInstance(url);
+        } catch (final Exception e) {
+            throw new IllegalStateException("Cannot use " + driver + " with parameter " + url, e);
+        }
+    }
+
+    private DataSource createDataSource(final DataSourceMetaData dataSourceMetaData) {
+        return new ClientDataSource(dataSourceMetaData);
+    }
+
+    private Object createWebservice(final WsMetaData webserviceMetaData) throws NamingException {
+        try {
+            return webserviceMetaData.createWebservice();
+        } catch (final Exception e) {
+            throw (NamingException) new NamingException("Error creating webservice").initCause(e);
+        }
+    }
+
+    private ORB getDefaultOrb() {
+        return ORB.init();
+    }
+
+    @Override
+    public Object lookup(final Name name) throws NamingException {
+        return lookup(name.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
+        if (name == null) {
+            throw new InvalidNameException("The name cannot be null");
+        } else if (name.startsWith("java:")) {
+            name = name.replaceFirst("^java:", "");
+        } else if (!name.startsWith("/")) {
+            name = tail + name;
+        }
+
+        final JNDIRequest req = new JNDIRequest(RequestMethodCode.JNDI_LIST, name);
+        req.setModuleId(moduleId);
+
+        final JNDIResponse res;
+        try {
+            res = request(req);
+        } catch (Exception e) {
+            if (e instanceof RemoteException && e.getCause() instanceof ConnectException) {
+                e = (Exception) e.getCause();
+                throw (ServiceUnavailableException) new ServiceUnavailableException("Cannot list '" + name + "'.").initCause(e);
+            }
+            throw (NamingException) new NamingException("Cannot list '" + name + "'.").initCause(e);
+        }
+
+        switch (res.getResponseCode()) {
+
+            case ResponseCodes.JNDI_OK:
+                return null;
+
+            case ResponseCodes.JNDI_ENUMERATION:
+                return (NamingEnumeration) res.getResult();
+
+            case ResponseCodes.JNDI_NOT_FOUND:
+                throw new NameNotFoundException(name);
+
+            case ResponseCodes.JNDI_NAMING_EXCEPTION:
+                final Throwable throwable = ((ThrowableArtifact) res.getResult()).getThrowable();
+                if (throwable instanceof NamingException) {
+                    throw (NamingException) throwable;
+                }
+                throw (NamingException) new NamingException().initCause(throwable);
+
+            case ResponseCodes.JNDI_ERROR:
+                throw (Error) res.getResult();
+
+            default:
+                throw new ClientRuntimeException("Invalid response from server :" + res.getResponseCode());
+        }
+
+    }
+
+    @Override
+    public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException {
+        return list(name.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public NamingEnumeration<Binding> listBindings(final String name) throws NamingException {
+        final Object o = lookup(name);
+        if (o instanceof Context) {
+            final Context context = (Context) o;
+            final NamingEnumeration<NameClassPair> enumeration = context.list("");
+            final List<NameClassPair> bindings = new ArrayList<NameClassPair>();
+
+            while (enumeration.hasMoreElements()) {
+                final NameClassPair pair = enumeration.nextElement();
+                bindings.add(new LazyBinding(pair.getName(), pair.getClassName(), context));
+            }
+
+            return new NameClassPairEnumeration(bindings);
+
+        } else {
+            return null;
+        }
+
+    }
+
+    private static class LazyBinding extends Binding {
+
+        private static final long serialVersionUID = 1L;
+        private RuntimeException failed;
+        private final Context context;
+
+        public LazyBinding(final String name, final String className, final Context context) {
+            super(name, className, null);
+            this.context = context;
+        }
+
+        @Override
+        public synchronized Object getObject() {
+            if (super.getObject() == null) {
+                if (failed != null) {
+                    throw failed;
+                }
+                try {
+                    super.setObject(context.lookup(getName()));
+                } catch (final NamingException e) {
+                    throw failed = new ClientRuntimeException("Failed to lazily fetch the binding '" + getName() + "'", e);
+                }
+            }
+            return super.getObject();
+        }
+    }
+
+    @Override
+    public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException {
+        return listBindings(name.toString());
+    }
+
+    @Override
+    public Object lookupLink(final String name) throws NamingException {
+        return lookup(name);
+    }
+
+    @Override
+    public Object lookupLink(final Name name) throws NamingException {
+        return lookupLink(name.toString());
+    }
+
+    @Override
+    public NameParser getNameParser(final String name) throws NamingException {
+        return new SimpleNameParser();
+    }
+
+    @Override
+    public NameParser getNameParser(final Name name) throws NamingException {
+        return new SimpleNameParser();
+    }
+
+    @Override
+    public String composeName(final String name, final String prefix) throws NamingException {
+        throw new OperationNotSupportedException("TODO: Needs to be implemented");
+    }
+
+    @Override
+    public Name composeName(final Name name, final Name prefix) throws NamingException {
+        throw new OperationNotSupportedException("TODO: Needs to be implemented");
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object addToEnvironment(final String key, final Object value) throws NamingException {
+        return env.put(key, value);
+    }
+
+    @Override
+    public Object removeFromEnvironment(final String key) throws NamingException {
+        return env.remove(key);
+    }
+
+    @Override
+    public Hashtable getEnvironment() throws NamingException {
+        return (Hashtable) env.clone();
+    }
+
+    @Override
+    public String getNameInNamespace() throws NamingException {
+        return "";
+    }
+
+    @Override
+    public void close() throws NamingException {
+        waitEndOfTasks(executorService);
+
+        final String userID = (String) env.get(Context.SECURITY_PRINCIPAL);
+        final String psswrd = (String) env.get(Context.SECURITY_CREDENTIALS);
+        this.authenticate(userID, psswrd, true);
+    }
+
+    private static void waitEndOfTasks(final ExecutorService executor) {
+        if (executor == null) {
+            return;
+        }
+
+        final List<Runnable> runnables = executor.shutdownNow();
+        for (final Runnable r : runnables) {
+            try {
+                r.run();
+            } catch (final Throwable th) {
+                LOGGER.log(Level.SEVERE, th.getMessage(), th);
+            }
+        }
+    }
+
+    @Override
+    public void bind(final String name, final Object obj) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public void bind(final Name name, final Object obj) throws NamingException {
+        bind(name.toString(), obj);
+    }
+
+    @Override
+    public void rebind(final String name, final Object obj) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public void rebind(final Name name, final Object obj) throws NamingException {
+        rebind(name.toString(), obj);
+    }
+
+    @Override
+    public void unbind(final String name) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public void unbind(final Name name) throws NamingException {
+        unbind(name.toString());
+    }
+
+    @Override
+    public void rename(final String oldname, final String newname) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public void rename(final Name oldname, final Name newname) throws NamingException {
+        rename(oldname.toString(), newname.toString());
+    }
+
+    @Override
+    public void destroySubcontext(final String name) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public void destroySubcontext(final Name name) throws NamingException {
+        destroySubcontext(name.toString());
+    }
+
+    @Override
+    public Context createSubcontext(final String name) throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    @Override
+    public Context createSubcontext(final Name name) throws NamingException {
+        return createSubcontext(name.toString());
+    }
+
+    private static final class SimpleNameParser implements NameParser {
+
+        private static final Properties PARSER_PROPERTIES = new Properties();
+
+        static {
+            PARSER_PROPERTIES.put("jndi.syntax.direction", "left_to_right");
+            PARSER_PROPERTIES.put("jndi.syntax.separator", "/");
+        }
+
+        private SimpleNameParser() {
+        }
+
+        @Override
+        public Name parse(final String name) throws NamingException {
+            return new CompoundName(name, PARSER_PROPERTIES);
+        }
+    }
+
+    public static class AuthenticationInfo implements Serializable {
+
+        private static final long serialVersionUID = -8898613592355280735L;
+        private final String realm;
+        private final String user;
+        private final char[] password;
+        private final long timeout;
+
+        public AuthenticationInfo(final String realm, final String user, final char[] password, final long timeout) {
+            this.realm = realm;
+            this.user = user;
+            this.password = password;
+            this.timeout = timeout;
+        }
+
+        public String getRealm() {
+            return realm;
+        }
+
+        public String getUser() {
+            return user;
+        }
+
+        public char[] getPassword() {
+            return password;
+        }
+
+        public long getTimeout() {
+            return timeout;
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/tomee/blob/ef7b52bd/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/AuthRequestHandler.java
----------------------------------------------------------------------
diff --git a/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/AuthRequestHandler.java b/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/AuthRequestHandler.java
index 3abaa1a..2d2f83b 100644
--- a/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/AuthRequestHandler.java
+++ b/server/openejb-ejbd/src/main/java/org/apache/openejb/server/ejbd/AuthRequestHandler.java
@@ -1,112 +1,119 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.openejb.server.ejbd;
-
-import org.apache.openejb.client.AuthenticationRequest;
-import org.apache.openejb.client.AuthenticationResponse;
-import org.apache.openejb.client.ClientMetaData;
-import org.apache.openejb.client.ProtocolMetaData;
-import org.apache.openejb.client.Response;
-import org.apache.openejb.client.ResponseCodes;
-import org.apache.openejb.loader.SystemInstance;
-import org.apache.openejb.spi.SecurityService;
-import org.apache.openejb.util.LogCategory;
-import org.apache.openejb.util.Logger;
-import org.apache.openejb.util.Messages;
-
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-class AuthRequestHandler extends RequestHandler {
-
-    Messages _messages = new Messages("org.apache.openejb.server.util.resources");
-    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_SERVER_REMOTE.createChild("auth"), "org.apache.openejb.server.util.resources");
-    private static final boolean debug = logger.isDebugEnabled();
-
-    protected AuthRequestHandler(final EjbDaemon daemon) {
-        super(daemon);
-    }
-
-    @Override
-    public String getName() {
-        return "Authentication";
-    }
-
-    @Override
-    public Logger getLogger() {
-        return logger;
-    }
-
-    @Override
-    public Response processRequest(final ObjectInputStream in, final ProtocolMetaData metaData) throws Exception {
-
-        final AuthenticationRequest req = new AuthenticationRequest();
-        req.setMetaData(metaData);
-
-        final AuthenticationResponse res = new AuthenticationResponse();
-        res.setMetaData(metaData);
-
-        try {
-            req.readExternal(in);
-
-            final String securityRealm = req.getRealm();
-            final String username = req.getUsername();
-            final String password = req.getCredentials();
-
-            final SecurityService securityService = SystemInstance.get().getComponent(SecurityService.class);
-            final Object token = securityService.login(securityRealm, username, password);
-
-            final ClientMetaData client = new ClientMetaData();
-            client.setMetaData(metaData);
-            client.setClientIdentity(token);
-
-            res.setIdentity(client);
-            res.setResponseCode(ResponseCodes.AUTH_GRANTED);
-        } catch (final Throwable t) {
-            res.setResponseCode(ResponseCodes.AUTH_DENIED);
-            res.setDeniedCause(t);
-        } finally {
-            if (debug) {
-                try {
-                    logger.debug("AUTH REQUEST: " + req + " -- RESPONSE: " + res);
-                } catch (final Exception e) {
-                    //Ignore
-                }
-            }
-        }
-
-        return res;
-    }
-
-    @Override
-    public void processResponse(final Response response, final ObjectOutputStream out, final ProtocolMetaData metaData) throws Exception {
-
-        if (AuthenticationResponse.class.isInstance(response)) {
-
-            final AuthenticationResponse res = (AuthenticationResponse) response;
-            res.setMetaData(metaData);
-
-            try {
-                res.writeExternal(out);
-            } catch (final Exception e) {
-                logger.fatal("Could not write AuthenticationResponse to output stream", e);
-            }
-        } else {
-            logger.error("AuthRequestHandler cannot process an instance of: " + response.getClass().getName());
-        }
-    }
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.server.ejbd;
+
+import org.apache.openejb.client.AuthenticationRequest;
+import org.apache.openejb.client.AuthenticationResponse;
+import org.apache.openejb.client.ClientMetaData;
+import org.apache.openejb.client.ProtocolMetaData;
+import org.apache.openejb.client.Response;
+import org.apache.openejb.client.ResponseCodes;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.SecurityService;
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
+import org.apache.openejb.util.Messages;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+class AuthRequestHandler extends RequestHandler {
+
+    Messages _messages = new Messages("org.apache.openejb.server.util.resources");
+    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_SERVER_REMOTE.createChild("auth"), "org.apache.openejb.server.util.resources");
+    private static final boolean debug = logger.isDebugEnabled();
+
+    protected AuthRequestHandler(final EjbDaemon daemon) {
+        super(daemon);
+    }
+
+    @Override
+    public String getName() {
+        return "Authentication";
+    }
+
+    @Override
+    public Logger getLogger() {
+        return logger;
+    }
+
+    @Override
+    public Response processRequest(final ObjectInputStream in, final ProtocolMetaData metaData) throws Exception {
+
+        final AuthenticationRequest req = new AuthenticationRequest();
+        req.setMetaData(metaData);
+
+        final AuthenticationResponse res = new AuthenticationResponse();
+        res.setMetaData(metaData);
+
+        try {
+            req.readExternal(in);
+
+            final String securityRealm = req.getRealm();
+            final String username = req.getUsername();
+            final String password = req.getCredentials();
+            final long timeout = req.getTimeout();
+            final boolean logout = req.isLogout();
+
+            final SecurityService securityService = SystemInstance.get().getComponent(SecurityService.class);
+
+            final Object token = securityService.login(securityRealm, username, password);
+
+            if(logout){
+                securityService.logout(token);
+            }
+
+            final ClientMetaData client = new ClientMetaData();
+            client.setMetaData(metaData);
+            client.setClientIdentity(token);
+
+            res.setIdentity(client);
+            res.setResponseCode(ResponseCodes.AUTH_GRANTED);
+        } catch (final Throwable t) {
+            res.setResponseCode(ResponseCodes.AUTH_DENIED);
+            res.setDeniedCause(t);
+        } finally {
+            if (debug) {
+                try {
+                    logger.debug("AUTH REQUEST: " + req + " -- RESPONSE: " + res);
+                } catch (final Exception e) {
+                    //Ignore
+                }
+            }
+        }
+
+        return res;
+    }
+
+    @Override
+    public void processResponse(final Response response, final ObjectOutputStream out, final ProtocolMetaData metaData) throws Exception {
+
+        if (AuthenticationResponse.class.isInstance(response)) {
+
+            final AuthenticationResponse res = (AuthenticationResponse) response;
+            res.setMetaData(metaData);
+
+            try {
+                res.writeExternal(out);
+            } catch (final Exception e) {
+                logger.fatal("Could not write AuthenticationResponse to output stream", e);
+            }
+        } else {
+            logger.error("AuthRequestHandler cannot process an instance of: " + response.getClass().getName());
+        }
+    }
 }
\ No newline at end of file


[2/2] tomee git commit: First hack at logout

Posted by an...@apache.org.
First hack at logout


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/ef7b52bd
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/ef7b52bd
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/ef7b52bd

Branch: refs/heads/tomee-1.7.x
Commit: ef7b52bdb2e380b8778bd27eb6c68bbd14764ca0
Parents: 809ed6b
Author: AndyGee <an...@gmx.de>
Authored: Wed Jul 1 22:38:47 2015 +0200
Committer: AndyGee <an...@gmx.de>
Committed: Wed Jul 1 22:38:47 2015 +0200

----------------------------------------------------------------------
 .../core/security/AbstractSecurityService.java  |  948 +++++-----
 .../StatefulSecurityPermissionsTest.java        |  604 +++----
 .../openejb/client/AuthenticationRequest.java   |  225 ++-
 .../org/apache/openejb/client/JNDIContext.java  | 1693 +++++++++---------
 .../openejb/server/ejbd/AuthRequestHandler.java |  229 +--
 5 files changed, 1887 insertions(+), 1812 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/ef7b52bd/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
index 2304230..8b8a3a6 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
@@ -1,468 +1,480 @@
-/*
- * 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.core.security;
-
-import org.apache.openejb.BeanContext;
-import org.apache.openejb.InterfaceType;
-import org.apache.openejb.core.ThreadContext;
-import org.apache.openejb.core.ThreadContextListener;
-import org.apache.openejb.core.security.jaas.GroupPrincipal;
-import org.apache.openejb.core.security.jacc.BasicJaccProvider;
-import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration;
-import org.apache.openejb.loader.SystemInstance;
-import org.apache.openejb.spi.CallerPrincipal;
-import org.apache.openejb.spi.SecurityService;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-import javax.security.jacc.EJBMethodPermission;
-import javax.security.jacc.PolicyConfigurationFactory;
-import javax.security.jacc.PolicyContext;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.security.AccessControlContext;
-import java.security.AccessControlException;
-import java.security.AccessController;
-import java.security.Policy;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * This security service chooses a UUID as its token as this can be serialized
- * to clients, is mostly secure, and can be deserialized in a client vm without
- * addition openejb-core classes.
- */
-public abstract class AbstractSecurityService implements SecurityService<UUID>, ThreadContextListener, BasicPolicyConfiguration.RoleResolver {
-
-    private static final Map<Object, Identity> identities = new ConcurrentHashMap<Object, Identity>();
-    protected static final ThreadLocal<Identity> clientIdentity = new ThreadLocal<Identity>();
-    protected String defaultUser = "guest";
-    private String realmName = "PropertiesLogin";
-    protected Subject defaultSubject;
-    protected SecurityContext defaultContext;
-
-    public AbstractSecurityService() {
-        this(BasicJaccProvider.class.getName());
-    }
-
-    public AbstractSecurityService(final String jaccProvider) {
-        System.setProperty(JaccProvider.class.getName(), jaccProvider);
-
-        installJacc();
-
-        ThreadContext.addThreadContextListener(this);
-
-        // set the default subject and the default context
-        updateSecurityContext();
-
-        SystemInstance.get().setComponent(BasicPolicyConfiguration.RoleResolver.class, this);
-    }
-
-    public String getRealmName() {
-        return realmName;
-    }
-
-    public void setRealmName(final String realmName) {
-        this.realmName = realmName;
-    }
-
-    /**
-     * @return the defaultUser
-     */
-    public String getDefaultUser() {
-        return defaultUser;
-    }
-
-    /**
-     * @param defaultUser the defaultUser to set
-     */
-    public void setDefaultUser(final String defaultUser) {
-        this.defaultUser = defaultUser;
-
-        // set the default subject and the default context for the new default user
-        updateSecurityContext();
-    }
-
-    // update the current subject and security context
-    private void updateSecurityContext() {
-        defaultSubject = createSubject(defaultUser, defaultUser);
-        defaultContext = new SecurityContext(defaultSubject);
-    }
-
-    @Override
-    public void init(final Properties props) throws Exception {
-    }
-
-    @Override
-    public UUID login(final String username, final String password) throws LoginException {
-        return login(realmName, username, password);
-    }
-
-    @Override
-    public Set<String> getLogicalRoles(final Principal[] principals, final Set<String> logicalRoles) {
-        final LinkedHashSet<String> roles = new LinkedHashSet<String>(principals.length);
-        for (final Principal principal : principals) {
-            final String name = principal.getName();
-            if (logicalRoles.contains(name)) {
-                roles.add(name);
-            }
-        }
-        return roles;
-    }
-
-    @Override
-    public void contextEntered(final ThreadContext oldContext, final ThreadContext newContext) {
-        final String moduleID = newContext.getBeanContext().getModuleID();
-        PolicyContext.setContextID(moduleID);
-
-        Subject runAsSubject = getRunAsSubject(newContext.getBeanContext());
-        if (oldContext != null && runAsSubject == null) {
-            runAsSubject = getRunAsSubject(oldContext.getBeanContext());
-        }
-
-        SecurityContext securityContext = oldContext != null ? oldContext.get(SecurityContext.class) : null;
-        if (runAsSubject != null) {
-
-            securityContext = new SecurityContext(runAsSubject);
-
-        } else if (securityContext == null) {
-
-            final Identity identity = clientIdentity.get();
-            if (identity != null) {
-                securityContext = new SecurityContext(identity.subject);
-            } else {
-                securityContext = defaultContext;
-            }
-        }
-
-        newContext.set(SecurityContext.class, securityContext);
-    }
-
-    protected Subject getRunAsSubject(final BeanContext callingBeanContext) {
-        if (callingBeanContext == null) {
-            return null;
-        }
-        return createRunAsSubject(callingBeanContext.getRunAsUser(), callingBeanContext.getRunAs());
-    }
-
-    protected Subject createRunAsSubject(final String runAsUser, final String runAsRole) {
-        return createSubject(runAsUser, runAsRole);
-    }
-
-    @Override
-    public void contextExited(final ThreadContext exitedContext, final ThreadContext reenteredContext) {
-        if (reenteredContext == null) {
-            PolicyContext.setContextID(null);
-        } else {
-            PolicyContext.setContextID(reenteredContext.getBeanContext().getModuleID());
-        }
-    }
-
-    protected UUID registerSubject(final Subject subject) {
-        final Identity identity = new Identity(subject);
-        final UUID token = identity.getToken();
-        identities.put(token, identity);
-        return token;
-    }
-
-    @Override
-    public void logout(final UUID securityIdentity) throws LoginException {
-        final Identity identity = identities.get(securityIdentity);
-        if (identity == null) {
-            throw new LoginException("Identity is not currently logged in: " + securityIdentity);
-        }
-        identities.remove(securityIdentity);
-    }
-
-    protected void unregisterSubject(final Object securityIdentity) {
-        identities.remove(securityIdentity);
-    }
-
-    @Override
-    public void associate(final UUID securityIdentity) throws LoginException {
-        if (clientIdentity.get() != null) {
-            throw new LoginException("Thread already associated with a client identity.  Refusing to overwrite.");
-        }
-        if (securityIdentity == null) {
-            throw new NullPointerException("The security token passed in is null");
-        }
-
-        // The securityIdentity token must associated with a logged in Identity
-        final Identity identity = identities.get(securityIdentity);
-        if (identity == null) {
-            throw new LoginException("Identity is not currently logged in: " + securityIdentity);
-        }
-
-        clientIdentity.set(identity);
-    }
-
-    @Override
-    public UUID disassociate() {
-        try {
-            final Identity identity = clientIdentity.get();
-            return identity == null ? null : identity.getToken();
-        } finally {
-            clientIdentity.remove();
-        }
-    }
-
-    @Override
-    public boolean isCallerInRole(final String role) {
-        if (role == null) {
-            throw new IllegalArgumentException("Role must not be null");
-        }
-
-        final ThreadContext threadContext = ThreadContext.getThreadContext();
-        if (threadContext == null) {
-            return false;
-        }
-
-        final SecurityContext securityContext = threadContext.get(SecurityContext.class);
-
-        final Set<Group> grps = securityContext.subject.getPrincipals(Group.class);
-        for (final Group grp : grps) {
-            if (grp.getName().equals(role)) {
-                return true;
-            }
-        }
-        final Set<GroupPrincipal> grpsp = securityContext.subject.getPrincipals(GroupPrincipal.class);
-        for (final GroupPrincipal grp : grpsp) {
-            if (grp.getName().equals(role)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public Principal getCallerPrincipal() {
-        final ThreadContext threadContext = ThreadContext.getThreadContext();
-        if (threadContext == null) {
-            final Identity id = clientIdentity.get();
-            if (id != null) {
-                return getCallerPrincipal(id.getSubject().getPrincipals());
-            }
-            return null;
-        }
-
-        final SecurityContext securityContext = threadContext.get(SecurityContext.class);
-        final Set<Principal> principals = securityContext.subject.getPrincipals();
-        return getCallerPrincipal(principals);
-    }
-
-    private Principal getCallerPrincipal(final Set<Principal> principals) {
-        if (!principals.isEmpty()) {
-            for (final Principal principal : principals) {
-                if (principal.getClass().isAnnotationPresent(CallerPrincipal.class)) {
-                    return principal;
-                }
-            }
-            return principals.iterator().next();
-        }
-        return null;
-    }
-
-    @Override
-    public boolean isCallerAuthorized(final Method method, final InterfaceType type) {
-        final ThreadContext threadContext = ThreadContext.getThreadContext();
-        final BeanContext beanContext = threadContext.getBeanContext();
-        try {
-            final String ejbName = beanContext.getEjbName();
-            String name = type == null ? null : type.getSpecName();
-            if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
-                name = null;
-            }
-            final Identity currentIdentity = clientIdentity.get();
-            final SecurityContext securityContext;
-            if (currentIdentity == null) {
-                securityContext = threadContext.get(SecurityContext.class);
-            } else {
-                securityContext = new SecurityContext(currentIdentity.getSubject());
-            }
-            securityContext.acc.checkPermission(new EJBMethodPermission(ejbName, name, method));
-        } catch (final AccessControlException e) {
-            return false;
-        }
-        return true;
-    }
-
-    protected static void installJacc() {
-        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-
-        final String providerKey = "javax.security.jacc.PolicyConfigurationFactory.provider";
-        try {
-            if (System.getProperty(providerKey) == null) {
-                System.setProperty(providerKey, JaccProvider.Factory.class.getName());
-                final ClassLoader cl = JaccProvider.Factory.class.getClassLoader();
-                Thread.currentThread().setContextClassLoader(cl);
-            }
-
-            // Force the loading of the javax.security.jacc.PolicyConfigurationFactory.provider
-            // Hopefully it will be cached thereafter and ClassNotFoundExceptions thrown
-            // from the equivalent call in JaccPermissionsBuilder can be avoided.
-            PolicyConfigurationFactory.getPolicyConfigurationFactory();
-        } catch (final Exception e) {
-            throw new IllegalStateException("Could not install JACC Policy Configuration Factory: " + System.getProperty(providerKey), e);
-        } finally {
-            Thread.currentThread().setContextClassLoader(contextClassLoader);
-        }
-
-        final String policyProvider = SystemInstance.get().getOptions().get("javax.security.jacc.policy.provider", JaccProvider.Policy.class.getName());
-        try {
-            final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-            final Class policyClass = Class.forName(policyProvider, true, classLoader);
-            final Policy policy = (Policy) policyClass.newInstance();
-            policy.refresh();
-            Policy.setPolicy(policy);
-        } catch (final Exception e) {
-            throw new IllegalStateException("Could not install JACC Policy Provider: " + policyProvider, e);
-        }
-    }
-
-    protected Subject createSubject(final String name, final String groupName) {
-        if (name == null) {
-            return null;
-        }
-
-        final User user = new User(name);
-        final Group group = new Group(groupName);
-        group.addMember(user);
-
-        final HashSet<Principal> principals = new HashSet<Principal>();
-        principals.add(user);
-        principals.add(group);
-
-        return new Subject(true, principals, new HashSet(), new HashSet());
-    }
-
-    @Override
-    public Object currentState() {
-        return clientIdentity.get();
-    }
-
-    @Override
-    public void setState(final Object o) {
-        if (Identity.class.isInstance(o)) {
-            clientIdentity.set(Identity.class.cast(o));
-        } else if (o == null) {
-            clientIdentity.remove();
-        }
-    }
-
-    protected static final class SecurityContext {
-
-        public final Subject subject;
-        public final AccessControlContext acc;
-
-        @SuppressWarnings("unchecked")
-        public SecurityContext(final Subject subject) {
-            this.subject = subject;
-            this.acc = (AccessControlContext) Subject.doAsPrivileged(subject, new PrivilegedAction() {
-                @Override
-                public Object run() {
-                    return AccessController.getContext();
-                }
-            }, null);
-        }
-    }
-
-    protected static class Identity implements Serializable {
-
-        private final Subject subject;
-        private final UUID token;
-
-        public Identity(final Subject subject) {
-            this.subject = subject;
-            this.token = UUID.randomUUID();
-        }
-
-        public Identity(final Subject subject, final UUID token) {
-            this.subject = subject;
-            this.token = token;
-        }
-
-        public Subject getSubject() {
-            return subject;
-        }
-
-        public UUID getToken() {
-            return token;
-        }
-    }
-
-    public static class Group implements java.security.acl.Group {
-
-        private final List<Principal> members = new ArrayList<Principal>();
-        private final String name;
-
-        public Group(final String name) {
-            this.name = name;
-        }
-
-        @Override
-        public boolean addMember(final Principal user) {
-            return members.add(user);
-        }
-
-        @Override
-        public boolean removeMember(final Principal user) {
-            return members.remove(user);
-        }
-
-        @Override
-        public boolean isMember(final Principal member) {
-            return members.contains(member);
-        }
-
-        @Override
-        public Enumeration<? extends Principal> members() {
-            return Collections.enumeration(members);
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-    }
-
-    @CallerPrincipal // to force it to be before group in getCallerPrincipal, otherwise we aren't deterministic
-    public static class User implements Principal {
-
-        private final String name;
-
-        public User(final String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-    }
-}
+/*
+ * 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.core.security;
+
+import org.apache.openejb.BeanContext;
+import org.apache.openejb.InterfaceType;
+import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.core.ThreadContextListener;
+import org.apache.openejb.core.security.jaas.GroupPrincipal;
+import org.apache.openejb.core.security.jacc.BasicJaccProvider;
+import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.CallerPrincipal;
+import org.apache.openejb.spi.SecurityService;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import javax.security.jacc.EJBMethodPermission;
+import javax.security.jacc.PolicyConfigurationFactory;
+import javax.security.jacc.PolicyContext;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.Policy;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This security service chooses a UUID as its token as this can be serialized
+ * to clients, is mostly secure, and can be deserialized in a client vm without
+ * addition openejb-core classes.
+ */
+public abstract class AbstractSecurityService implements SecurityService<UUID>, ThreadContextListener, BasicPolicyConfiguration.RoleResolver {
+
+    private static final Map<UUID, Identity> identities = new ConcurrentHashMap<UUID, Identity>();
+    protected static final ThreadLocal<Identity> clientIdentity = new ThreadLocal<Identity>();
+    protected String defaultUser = "guest";
+    private String realmName = "PropertiesLogin";
+    protected Subject defaultSubject;
+    protected SecurityContext defaultContext;
+
+    public AbstractSecurityService() {
+        this(BasicJaccProvider.class.getName());
+    }
+
+    public AbstractSecurityService(final String jaccProvider) {
+        System.setProperty(JaccProvider.class.getName(), jaccProvider);
+
+        installJacc();
+
+        ThreadContext.addThreadContextListener(this);
+
+        // set the default subject and the default context
+        updateSecurityContext();
+
+        SystemInstance.get().setComponent(BasicPolicyConfiguration.RoleResolver.class, this);
+    }
+
+    public String getRealmName() {
+        return realmName;
+    }
+
+    public void setRealmName(final String realmName) {
+        this.realmName = realmName;
+    }
+
+    /**
+     * @return the defaultUser
+     */
+    public String getDefaultUser() {
+        return defaultUser;
+    }
+
+    /**
+     * @param defaultUser the defaultUser to set
+     */
+    public void setDefaultUser(final String defaultUser) {
+        this.defaultUser = defaultUser;
+
+        // set the default subject and the default context for the new default user
+        updateSecurityContext();
+    }
+
+    // update the current subject and security context
+    private void updateSecurityContext() {
+        defaultSubject = createSubject(defaultUser, defaultUser);
+        defaultContext = new SecurityContext(defaultSubject);
+    }
+
+    @Override
+    public void init(final Properties props) throws Exception {
+    }
+
+    @Override
+    public UUID login(final String username, final String password) throws LoginException {
+        return login(realmName, username, password);
+    }
+
+    @Override
+    public Set<String> getLogicalRoles(final Principal[] principals, final Set<String> logicalRoles) {
+        final LinkedHashSet<String> roles = new LinkedHashSet<String>(principals.length);
+        for (final Principal principal : principals) {
+            final String name = principal.getName();
+            if (logicalRoles.contains(name)) {
+                roles.add(name);
+            }
+        }
+        return roles;
+    }
+
+    @Override
+    public void contextEntered(final ThreadContext oldContext, final ThreadContext newContext) {
+        final String moduleID = newContext.getBeanContext().getModuleID();
+        PolicyContext.setContextID(moduleID);
+
+        Subject runAsSubject = getRunAsSubject(newContext.getBeanContext());
+        if (oldContext != null && runAsSubject == null) {
+            runAsSubject = getRunAsSubject(oldContext.getBeanContext());
+        }
+
+        SecurityContext securityContext = oldContext != null ? oldContext.get(SecurityContext.class) : null;
+        if (runAsSubject != null) {
+
+            securityContext = new SecurityContext(runAsSubject);
+
+        } else if (securityContext == null) {
+
+            final Identity identity = clientIdentity.get();
+            if (identity != null) {
+                securityContext = new SecurityContext(identity.subject);
+            } else {
+                securityContext = defaultContext;
+            }
+        }
+
+        newContext.set(SecurityContext.class, securityContext);
+    }
+
+    protected Subject getRunAsSubject(final BeanContext callingBeanContext) {
+        if (callingBeanContext == null) {
+            return null;
+        }
+        return createRunAsSubject(callingBeanContext.getRunAsUser(), callingBeanContext.getRunAs());
+    }
+
+    protected Subject createRunAsSubject(final String runAsUser, final String runAsRole) {
+        return createSubject(runAsUser, runAsRole);
+    }
+
+    @Override
+    public void contextExited(final ThreadContext exitedContext, final ThreadContext reenteredContext) {
+        if (reenteredContext == null) {
+            PolicyContext.setContextID(null);
+        } else {
+            PolicyContext.setContextID(reenteredContext.getBeanContext().getModuleID());
+        }
+    }
+
+    protected UUID registerSubject(final Subject subject) {
+        final Identity identity = new Identity(subject);
+        final UUID token = identity.getToken();
+        identities.put(token, identity);
+        return token;
+    }
+
+    @Override
+    public void logout(final UUID securityIdentity) throws LoginException {
+        final Identity identity = identities.get(securityIdentity);
+        if (identity == null) {
+            throw new LoginException("Identity is not currently logged in: " + securityIdentity);
+        }
+        identities.remove(securityIdentity);
+    }
+
+    protected void unregisterSubject(final Object securityIdentity) {
+        identities.remove(securityIdentity);
+    }
+
+    @Override
+    public void associate(final UUID securityIdentity) throws LoginException {
+        if (clientIdentity.get() != null) {
+            throw new LoginException("Thread already associated with a client identity.  Refusing to overwrite.");
+        }
+        if (securityIdentity == null) {
+            throw new NullPointerException("The security token passed in is null");
+        }
+
+        // The securityIdentity token must associated with a logged in Identity
+        final Identity identity = identities.get(securityIdentity);
+        if (identity == null) {
+            throw new LoginException("Identity is not currently logged in: " + securityIdentity);
+        }
+
+        clientIdentity.set(identity);
+    }
+
+    @Override
+    public UUID disassociate() {
+        try {
+            final Identity identity = clientIdentity.get();
+            return identity == null ? null : identity.getToken();
+        } finally {
+            clientIdentity.remove();
+        }
+    }
+
+    @Override
+    public boolean isCallerInRole(final String role) {
+        if (role == null) {
+            throw new IllegalArgumentException("Role must not be null");
+        }
+
+        final ThreadContext threadContext = ThreadContext.getThreadContext();
+        if (threadContext == null) {
+            return false;
+        }
+
+        final SecurityContext securityContext = threadContext.get(SecurityContext.class);
+
+        final Set<Group> grps = securityContext.subject.getPrincipals(Group.class);
+        for (final Group grp : grps) {
+            if (grp.getName().equals(role)) {
+                return true;
+            }
+        }
+        final Set<GroupPrincipal> grpsp = securityContext.subject.getPrincipals(GroupPrincipal.class);
+        for (final GroupPrincipal grp : grpsp) {
+            if (grp.getName().equals(role)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Principal getCallerPrincipal() {
+        final ThreadContext threadContext = ThreadContext.getThreadContext();
+        if (threadContext == null) {
+            final Identity id = clientIdentity.get();
+            if (id != null) {
+                return getCallerPrincipal(id.getSubject().getPrincipals());
+            }
+            return null;
+        }
+
+        final SecurityContext securityContext = threadContext.get(SecurityContext.class);
+        final Set<Principal> principals = securityContext.subject.getPrincipals();
+        return getCallerPrincipal(principals);
+    }
+
+    private Principal getCallerPrincipal(final Set<Principal> principals) {
+        if (!principals.isEmpty()) {
+            for (final Principal principal : principals) {
+                if (principal.getClass().isAnnotationPresent(CallerPrincipal.class)) {
+                    return principal;
+                }
+            }
+            return principals.iterator().next();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isCallerAuthorized(final Method method, final InterfaceType type) {
+        final ThreadContext threadContext = ThreadContext.getThreadContext();
+        final BeanContext beanContext = threadContext.getBeanContext();
+        try {
+            final String ejbName = beanContext.getEjbName();
+            String name = type == null ? null : type.getSpecName();
+            if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
+                name = null;
+            }
+            final Identity currentIdentity = clientIdentity.get();
+            final SecurityContext securityContext;
+            if (currentIdentity == null) {
+                securityContext = threadContext.get(SecurityContext.class);
+            } else {
+                securityContext = new SecurityContext(currentIdentity.getSubject());
+            }
+            securityContext.acc.checkPermission(new EJBMethodPermission(ejbName, name, method));
+        } catch (final AccessControlException e) {
+            return false;
+        }
+        return true;
+    }
+
+    protected static void installJacc() {
+        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+        final String providerKey = "javax.security.jacc.PolicyConfigurationFactory.provider";
+        try {
+            if (System.getProperty(providerKey) == null) {
+                System.setProperty(providerKey, JaccProvider.Factory.class.getName());
+                final ClassLoader cl = JaccProvider.Factory.class.getClassLoader();
+                Thread.currentThread().setContextClassLoader(cl);
+            }
+
+            // Force the loading of the javax.security.jacc.PolicyConfigurationFactory.provider
+            // Hopefully it will be cached thereafter and ClassNotFoundExceptions thrown
+            // from the equivalent call in JaccPermissionsBuilder can be avoided.
+            PolicyConfigurationFactory.getPolicyConfigurationFactory();
+        } catch (final Exception e) {
+            throw new IllegalStateException("Could not install JACC Policy Configuration Factory: " + System.getProperty(providerKey), e);
+        } finally {
+            Thread.currentThread().setContextClassLoader(contextClassLoader);
+        }
+
+        final String policyProvider = SystemInstance.get().getOptions().get("javax.security.jacc.policy.provider", JaccProvider.Policy.class.getName());
+        try {
+            final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            final Class policyClass = Class.forName(policyProvider, true, classLoader);
+            final Policy policy = (Policy) policyClass.newInstance();
+            policy.refresh();
+            Policy.setPolicy(policy);
+        } catch (final Exception e) {
+            throw new IllegalStateException("Could not install JACC Policy Provider: " + policyProvider, e);
+        }
+    }
+
+    protected Subject createSubject(final String name, final String groupName) {
+        if (name == null) {
+            return null;
+        }
+
+        final User user = new User(name);
+        final Group group = new Group(groupName);
+        group.addMember(user);
+
+        final HashSet<Principal> principals = new HashSet<Principal>();
+        principals.add(user);
+        principals.add(group);
+
+        return new Subject(true, principals, new HashSet(), new HashSet());
+    }
+
+    @Override
+    public Object currentState() {
+        return clientIdentity.get();
+    }
+
+    @Override
+    public void setState(final Object o) {
+        if (Identity.class.isInstance(o)) {
+            clientIdentity.set(Identity.class.cast(o));
+        } else if (o == null) {
+            clientIdentity.remove();
+        }
+    }
+
+    protected static final class SecurityContext {
+
+        public final Subject subject;
+        public final AccessControlContext acc;
+
+        @SuppressWarnings("unchecked")
+        public SecurityContext(final Subject subject) {
+            this.subject = subject;
+            this.acc = (AccessControlContext) Subject.doAsPrivileged(subject, new PrivilegedAction() {
+                @Override
+                public Object run() {
+                    return AccessController.getContext();
+                }
+            }, null);
+        }
+    }
+
+    protected static class Identity implements Serializable {
+
+        private long lastAccess;
+        private final Subject subject;
+        private final UUID token;
+
+        public Identity(final Subject subject) {
+            this.subject = subject;
+            this.token = UUID.randomUUID();
+            access();
+        }
+
+        public Identity(final Subject subject, final UUID token) {
+            this.subject = subject;
+            this.token = token;
+        }
+
+        private void access() {
+            this.lastAccess = System.currentTimeMillis();
+        }
+
+        public Subject getSubject() {
+            access();
+            return subject;
+        }
+
+        public UUID getToken() {
+            access();
+            return token;
+        }
+
+        public long getLastAccess() {
+            return lastAccess;
+        }
+    }
+
+    public static class Group implements java.security.acl.Group {
+
+        private final List<Principal> members = new ArrayList<Principal>();
+        private final String name;
+
+        public Group(final String name) {
+            this.name = name;
+        }
+
+        @Override
+        public boolean addMember(final Principal user) {
+            return members.add(user);
+        }
+
+        @Override
+        public boolean removeMember(final Principal user) {
+            return members.remove(user);
+        }
+
+        @Override
+        public boolean isMember(final Principal member) {
+            return members.contains(member);
+        }
+
+        @Override
+        public Enumeration<? extends Principal> members() {
+            return Collections.enumeration(members);
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+
+    @CallerPrincipal // to force it to be before group in getCallerPrincipal, otherwise we aren't deterministic
+    public static class User implements Principal {
+
+        private final String name;
+
+        public User(final String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/ef7b52bd/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java b/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java
index ff371cf..db3bc32 100644
--- a/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java
+++ b/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java
@@ -1,302 +1,302 @@
-/**
- * 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.core.stateful;
-
-import junit.framework.TestCase;
-import org.apache.openejb.OpenEJB;
-import org.apache.openejb.assembler.classic.Assembler;
-import org.apache.openejb.assembler.classic.EjbJarInfo;
-import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
-import org.apache.openejb.assembler.classic.SecurityServiceInfo;
-import org.apache.openejb.assembler.classic.TransactionServiceInfo;
-import org.apache.openejb.config.ConfigurationFactory;
-import org.apache.openejb.core.ivm.naming.InitContextFactory;
-import org.apache.openejb.core.security.AbstractSecurityService;
-import org.apache.openejb.core.security.jaas.GroupPrincipal;
-import org.apache.openejb.core.security.jaas.UserPrincipal;
-import org.apache.openejb.core.security.jacc.BasicJaccProvider;
-import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration;
-import org.apache.openejb.jee.EjbJar;
-import org.apache.openejb.jee.MethodPermission;
-import org.apache.openejb.jee.StatefulBean;
-import org.apache.openejb.loader.SystemInstance;
-import org.apache.openejb.spi.SecurityService;
-import org.junit.AfterClass;
-
-import javax.annotation.security.DenyAll;
-import javax.annotation.security.PermitAll;
-import javax.annotation.security.RolesAllowed;
-import javax.ejb.CreateException;
-import javax.ejb.EJBAccessException;
-import javax.ejb.EJBHome;
-import javax.ejb.EJBLocalHome;
-import javax.ejb.EJBLocalObject;
-import javax.ejb.EJBObject;
-import javax.ejb.Init;
-import javax.ejb.Local;
-import javax.ejb.LocalHome;
-import javax.ejb.Remote;
-import javax.ejb.RemoteHome;
-import javax.ejb.Remove;
-import javax.naming.InitialContext;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-import java.rmi.RemoteException;
-import java.security.Permission;
-import java.security.PermissionCollection;
-import java.security.Principal;
-import java.security.ProtectionDomain;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * @version $Rev$ $Date$
- */
-public class StatefulSecurityPermissionsTest extends TestCase {
-
-    @AfterClass
-    public static void afterClass() throws Exception {
-        OpenEJB.destroy();
-    }
-
-    public void test() throws Exception {
-        System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, InitContextFactory.class.getName());
-
-        final Assembler assembler = new Assembler();
-        final ConfigurationFactory config = new ConfigurationFactory();
-
-        assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class));
-        assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
-        final SecurityServiceInfo securityServiceInfo = config.configureService(SecurityServiceInfo.class);
-        securityServiceInfo.className = TestSecurityService.class.getName();
-        assembler.createSecurityService(securityServiceInfo);
-
-        final TestSecurityService securityService = (TestSecurityService) SystemInstance.get().getComponent(SecurityService.class);
-
-        securityService.login("foo", "Jazz", "Rock", "Reggae", "HipHop");
-
-        final EjbJar ejbJar = new EjbJar();
-        ejbJar.addEnterpriseBean(new StatefulBean(Color.class));
-        final List<MethodPermission> permissions = ejbJar.getAssemblyDescriptor().getMethodPermission();
-        permissions.add(new MethodPermission("*", "Color", "*", "Foo"));
-        permissions.add(new MethodPermission("*", "Color", "create").setUnchecked());
-        permissions.add(new MethodPermission("*", "Color", "ejbCreate").setUnchecked());
-
-        final EjbJarInfo ejbJarInfo = config.configureApplication(ejbJar);
-        assembler.createApplication(ejbJarInfo);
-
-        final InitialContext context = new InitialContext();
-
-        {
-            final ColorLocal color = (ColorLocal) context.lookup("ColorLocal");
-
-            assertEquals("Jazz", color.color());
-            try {
-                color.color((Object) null);
-            } catch (final EJBAccessException e) {
-                assertEquals("Excluded", actual.get());
-            }
-            assertEquals("Rock", color.color((String) null));
-            assertEquals("Unchecked", color.color((Boolean) null));
-            assertEquals("Reggae", color.color((Integer) null));
-        }
-
-    }
-
-    public static ThreadLocal<String> expected = new ThreadLocal<String>();
-
-    @LocalHome(ColorEjbLocalHome.class)
-    @RemoteHome(ColorEjbHome.class)
-    public static class Color implements ColorLocal, ColorRemote {
-
-        protected String attribute() {
-            return actual.get();
-        }
-
-        @Init
-        public void ejbCreate(final String s) {
-            assertEquals(s, attribute());
-        }
-
-        @Remove
-        public void ejbRemove() {
-            assertEquals(expected.get(), attribute());
-        }
-
-
-        @RolesAllowed({"Jazz"})
-        public String color() {
-            return attribute();
-        }
-
-        @DenyAll
-        public String color(final Object o) {
-            return attribute();
-        }
-
-        @RolesAllowed({"Rock"})
-        public String color(final String s) {
-            return attribute();
-        }
-
-        @PermitAll
-        public String color(final Boolean b) {
-            return attribute();
-        }
-
-        @RolesAllowed({"Reggae"})
-        public String color(final Integer i) {
-            return attribute();
-        }
-
-
-    }
-
-    @Local
-    public static interface ColorLocal {
-        public String color();
-
-        public String color(Object o);
-
-        public String color(String s);
-
-        public String color(Boolean b);
-
-        public String color(Integer i);
-    }
-
-    @Remote
-    public static interface ColorRemote {
-        public String color();
-
-        public String color(Object o);
-
-        public String color(String s);
-
-        public String color(Boolean b);
-
-        public String color(Integer i);
-    }
-
-    public static interface ColorEjbHome extends EJBHome {
-        ColorEjbObject create(String s) throws CreateException, RemoteException;
-    }
-
-    public static interface ColorEjbObject extends EJBObject {
-        public String color() throws RemoteException;
-
-        public String color(Object o) throws RemoteException;
-
-        public String color(String s) throws RemoteException;
-
-        public String color(Boolean b) throws RemoteException;
-
-        public String color(Integer i) throws RemoteException;
-    }
-
-    public static interface ColorEjbLocalHome extends EJBLocalHome {
-        ColorEjbLocalObject create(String s) throws CreateException;
-    }
-
-    public static interface ColorEjbLocalObject extends EJBLocalObject {
-        public String color();
-
-        public String color(Object o);
-
-        public String color(String s);
-
-        public String color(Boolean b);
-
-        public String color(Integer i);
-    }
-
-
-    private static ThreadLocal<String> actual = new ThreadLocal<String>();
-
-    public static class TestSecurityService extends AbstractSecurityService {
-
-        public TestSecurityService() {
-            super(TestJaccProvider.class.getName());
-        }
-
-        public UUID login(final String securityRealm, final String user, final String pass) throws LoginException {
-            return null;
-        }
-
-        public void login(final String user, final String... roles) throws LoginException {
-            final Set<Principal> set = new HashSet<Principal>();
-            set.add(new UserPrincipal(user));
-            for (final String role : roles) {
-                set.add(new GroupPrincipal(role));
-            }
-            final Subject subject = new Subject(true, set, Collections.EMPTY_SET, Collections.EMPTY_SET);
-            final UUID uuid = registerSubject(subject);
-            associate(uuid);
-        }
-
-        public void logout() {
-            this.disassociate();
-        }
-
-        public static class TestJaccProvider extends BasicJaccProvider {
-            protected BasicPolicyConfiguration createPolicyConfiguration(final String contextID) {
-                return new TestPolicy(contextID);
-            }
-
-            public static class TestPolicy extends BasicPolicyConfiguration {
-
-                TestPolicy(final String contextID) {
-                    super(contextID);
-                }
-
-                public boolean implies(final ProtectionDomain domain, final Permission permission) {
-
-                    if (excluded != null && excluded.implies(permission)) {
-                        actual.set("Excluded");
-                        return false;
-                    }
-
-                    if (unchecked != null && unchecked.implies(permission)) {
-                        actual.set("Unchecked");
-                        return true;
-                    }
-
-                    final Principal[] principals = domain.getPrincipals();
-                    if (principals.length == 0) return false;
-
-                    final RoleResolver roleResolver = SystemInstance.get().getComponent(RoleResolver.class);
-                    final Set<String> roles = roleResolver.getLogicalRoles(principals, rolePermissionsMap.keySet());
-
-                    for (final String role : roles) {
-                        final PermissionCollection permissions = rolePermissionsMap.get(role);
-
-                        if (permissions != null && permissions.implies(permission)) {
-                            actual.set(role);
-                            return true;
-                        }
-                    }
-
-                    actual.set("Denied");
-                    return false;
-                }
-            }
-        }
-    }
-}
+/**
+ * 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.core.stateful;
+
+import junit.framework.TestCase;
+import org.apache.openejb.OpenEJB;
+import org.apache.openejb.assembler.classic.Assembler;
+import org.apache.openejb.assembler.classic.EjbJarInfo;
+import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
+import org.apache.openejb.assembler.classic.SecurityServiceInfo;
+import org.apache.openejb.assembler.classic.TransactionServiceInfo;
+import org.apache.openejb.config.ConfigurationFactory;
+import org.apache.openejb.core.ivm.naming.InitContextFactory;
+import org.apache.openejb.core.security.AbstractSecurityService;
+import org.apache.openejb.core.security.jaas.GroupPrincipal;
+import org.apache.openejb.core.security.jaas.UserPrincipal;
+import org.apache.openejb.core.security.jacc.BasicJaccProvider;
+import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.MethodPermission;
+import org.apache.openejb.jee.StatefulBean;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.SecurityService;
+import org.junit.AfterClass;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+import javax.ejb.CreateException;
+import javax.ejb.EJBAccessException;
+import javax.ejb.EJBHome;
+import javax.ejb.EJBLocalHome;
+import javax.ejb.EJBLocalObject;
+import javax.ejb.EJBObject;
+import javax.ejb.Init;
+import javax.ejb.Local;
+import javax.ejb.LocalHome;
+import javax.ejb.Remote;
+import javax.ejb.RemoteHome;
+import javax.ejb.Remove;
+import javax.naming.InitialContext;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import java.rmi.RemoteException;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Principal;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class StatefulSecurityPermissionsTest extends TestCase {
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        OpenEJB.destroy();
+    }
+
+    public void test() throws Exception {
+        System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, InitContextFactory.class.getName());
+
+        final Assembler assembler = new Assembler();
+        final ConfigurationFactory config = new ConfigurationFactory();
+
+        assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class));
+        assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
+        final SecurityServiceInfo securityServiceInfo = config.configureService(SecurityServiceInfo.class);
+        securityServiceInfo.className = TestSecurityService.class.getName();
+        assembler.createSecurityService(securityServiceInfo);
+
+        final TestSecurityService securityService = (TestSecurityService) SystemInstance.get().getComponent(SecurityService.class);
+
+        securityService.login("foo", "Jazz", "Rock", "Reggae", "HipHop");
+
+        final EjbJar ejbJar = new EjbJar();
+        ejbJar.addEnterpriseBean(new StatefulBean(Color.class));
+        final List<MethodPermission> permissions = ejbJar.getAssemblyDescriptor().getMethodPermission();
+        permissions.add(new MethodPermission("*", "Color", "*", "Foo"));
+        permissions.add(new MethodPermission("*", "Color", "create").setUnchecked());
+        permissions.add(new MethodPermission("*", "Color", "ejbCreate").setUnchecked());
+
+        final EjbJarInfo ejbJarInfo = config.configureApplication(ejbJar);
+        assembler.createApplication(ejbJarInfo);
+
+        final InitialContext context = new InitialContext();
+
+        {
+            final ColorLocal color = (ColorLocal) context.lookup("ColorLocal");
+
+            assertEquals("Jazz", color.color());
+            try {
+                color.color((Object) null);
+            } catch (final EJBAccessException e) {
+                assertEquals("Excluded", actual.get());
+            }
+            assertEquals("Rock", color.color((String) null));
+            assertEquals("Unchecked", color.color((Boolean) null));
+            assertEquals("Reggae", color.color((Integer) null));
+        }
+
+    }
+
+    public static ThreadLocal<String> expected = new ThreadLocal<String>();
+
+    @LocalHome(ColorEjbLocalHome.class)
+    @RemoteHome(ColorEjbHome.class)
+    public static class Color implements ColorLocal, ColorRemote {
+
+        protected String attribute() {
+            return actual.get();
+        }
+
+        @Init
+        public void ejbCreate(final String s) {
+            assertEquals(s, attribute());
+        }
+
+        @Remove
+        public void ejbRemove() {
+            assertEquals(expected.get(), attribute());
+        }
+
+
+        @RolesAllowed({"Jazz"})
+        public String color() {
+            return attribute();
+        }
+
+        @DenyAll
+        public String color(final Object o) {
+            return attribute();
+        }
+
+        @RolesAllowed({"Rock"})
+        public String color(final String s) {
+            return attribute();
+        }
+
+        @PermitAll
+        public String color(final Boolean b) {
+            return attribute();
+        }
+
+        @RolesAllowed({"Reggae"})
+        public String color(final Integer i) {
+            return attribute();
+        }
+
+
+    }
+
+    @Local
+    public static interface ColorLocal {
+        public String color();
+
+        public String color(Object o);
+
+        public String color(String s);
+
+        public String color(Boolean b);
+
+        public String color(Integer i);
+    }
+
+    @Remote
+    public static interface ColorRemote {
+        public String color();
+
+        public String color(Object o);
+
+        public String color(String s);
+
+        public String color(Boolean b);
+
+        public String color(Integer i);
+    }
+
+    public static interface ColorEjbHome extends EJBHome {
+        ColorEjbObject create(String s) throws CreateException, RemoteException;
+    }
+
+    public static interface ColorEjbObject extends EJBObject {
+        public String color() throws RemoteException;
+
+        public String color(Object o) throws RemoteException;
+
+        public String color(String s) throws RemoteException;
+
+        public String color(Boolean b) throws RemoteException;
+
+        public String color(Integer i) throws RemoteException;
+    }
+
+    public static interface ColorEjbLocalHome extends EJBLocalHome {
+        ColorEjbLocalObject create(String s) throws CreateException;
+    }
+
+    public static interface ColorEjbLocalObject extends EJBLocalObject {
+        public String color();
+
+        public String color(Object o);
+
+        public String color(String s);
+
+        public String color(Boolean b);
+
+        public String color(Integer i);
+    }
+
+
+    private static final ThreadLocal<String> actual = new ThreadLocal<String>();
+
+    public static class TestSecurityService extends AbstractSecurityService {
+
+        public TestSecurityService() {
+            super(TestJaccProvider.class.getName());
+        }
+
+        public UUID login(final String securityRealm, final String user, final String pass) throws LoginException {
+            return null;
+        }
+
+        public void login(final String user, final String... roles) throws LoginException {
+            final Set<Principal> set = new HashSet<Principal>();
+            set.add(new UserPrincipal(user));
+            for (final String role : roles) {
+                set.add(new GroupPrincipal(role));
+            }
+            final Subject subject = new Subject(true, set, Collections.EMPTY_SET, Collections.EMPTY_SET);
+            final UUID uuid = registerSubject(subject);
+            associate(uuid);
+        }
+
+        public void logout() {
+            this.disassociate();
+        }
+
+        public static class TestJaccProvider extends BasicJaccProvider {
+            protected BasicPolicyConfiguration createPolicyConfiguration(final String contextID) {
+                return new TestPolicy(contextID);
+            }
+
+            public static class TestPolicy extends BasicPolicyConfiguration {
+
+                TestPolicy(final String contextID) {
+                    super(contextID);
+                }
+
+                public boolean implies(final ProtectionDomain domain, final Permission permission) {
+
+                    if (excluded != null && excluded.implies(permission)) {
+                        actual.set("Excluded");
+                        return false;
+                    }
+
+                    if (unchecked != null && unchecked.implies(permission)) {
+                        actual.set("Unchecked");
+                        return true;
+                    }
+
+                    final Principal[] principals = domain.getPrincipals();
+                    if (principals.length == 0) return false;
+
+                    final RoleResolver roleResolver = SystemInstance.get().getComponent(RoleResolver.class);
+                    final Set<String> roles = roleResolver.getLogicalRoles(principals, rolePermissionsMap.keySet());
+
+                    for (final String role : roles) {
+                        final PermissionCollection permissions = rolePermissionsMap.get(role);
+
+                        if (permissions != null && permissions.implies(permission)) {
+                            actual.set(role);
+                            return true;
+                        }
+                    }
+
+                    actual.set("Denied");
+                    return false;
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/ef7b52bd/server/openejb-client/src/main/java/org/apache/openejb/client/AuthenticationRequest.java
----------------------------------------------------------------------
diff --git a/server/openejb-client/src/main/java/org/apache/openejb/client/AuthenticationRequest.java b/server/openejb-client/src/main/java/org/apache/openejb/client/AuthenticationRequest.java
index 686baa6..ea49446 100644
--- a/server/openejb-client/src/main/java/org/apache/openejb/client/AuthenticationRequest.java
+++ b/server/openejb-client/src/main/java/org/apache/openejb/client/AuthenticationRequest.java
@@ -1,99 +1,126 @@
-/**
- * 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.IOException;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
-
-public class AuthenticationRequest implements Request {
-
-    private static final long serialVersionUID = 7009531340198948330L;
-    private transient String realm;
-    private transient String username;
-    private transient String credentials;
-    private transient ProtocolMetaData metaData;
-
-    public AuthenticationRequest() {
-    }
-
-    public AuthenticationRequest(final String principal, final String credentials) {
-        this(null, principal, credentials);
-    }
-
-    public AuthenticationRequest(final String realm, final String principal, final String credentials) {
-        this.realm = realm;
-        this.username = principal;
-        this.credentials = credentials;
-    }
-
-    @Override
-    public void setMetaData(final ProtocolMetaData metaData) {
-        this.metaData = metaData;
-    }
-
-    @Override
-    public RequestType getRequestType() {
-        return RequestType.AUTH_REQUEST;
-    }
-
-    public String getRealm() {
-        return realm;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public String getCredentials() {
-        return credentials;
-    }
-
-    /**
-     * Changes to this method must observe the optional {@link #metaData} version
-     */
-    @Override
-    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
-        final byte version = in.readByte(); // future use
-
-        realm = (String) in.readObject();
-        username = (String) in.readObject();
-        credentials = (String) in.readObject();
-    }
-
-    /**
-     * Changes to this method must observe the optional {@link #metaData} version
-     */
-    @Override
-    public void writeExternal(final ObjectOutput out) throws IOException {
-        // write out the version of the serialized data for future use
-        out.writeByte(1);
-
-        out.writeObject(realm);
-        out.writeObject(username);
-        out.writeObject(credentials);
-    }
-
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(50);
-        sb.append(null != realm ? realm : "Unknown realm").append(':');
-        sb.append(null != username ? username : "Unknown user").append(':');
-        sb.append(null != credentials ? credentials : "Unknown credentials");
-        return sb.toString();
-    }
-}
-
+/**
+ * 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.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+public class AuthenticationRequest implements Request {
+
+    private static final long serialVersionUID = 7009531340098948330L;
+    private transient String realm;
+    private transient String username;
+    private transient String credentials;
+    private transient long timeout;
+    private transient ProtocolMetaData metaData;
+    private transient boolean logout = false;
+
+    public AuthenticationRequest() {
+    }
+
+    public AuthenticationRequest(final String principal, final String credentials) {
+        this(null, principal, credentials, 0);
+    }
+
+    public AuthenticationRequest(final String principal, final String credentials, final long timeout) {
+        this(null, principal, credentials, timeout);
+    }
+
+    public AuthenticationRequest(final String realm, final String principal, final String credentials, final long timeout) {
+        this.realm = realm;
+        this.username = principal;
+        this.credentials = credentials;
+        this.timeout = timeout;
+    }
+
+    @Override
+    public void setMetaData(final ProtocolMetaData metaData) {
+        this.metaData = metaData;
+    }
+
+    @Override
+    public RequestType getRequestType() {
+        return RequestType.AUTH_REQUEST;
+    }
+
+    public String getRealm() {
+        return realm;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getCredentials() {
+        return credentials;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    public boolean isLogout() {
+        return logout;
+    }
+
+    public void setLogout(final boolean logout) {
+        this.logout = logout;
+    }
+
+    /**
+     * Changes to this method must observe the optional {@link #metaData} version
+     */
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final byte version = in.readByte(); // future use
+
+        realm = (String) in.readObject();
+        username = (String) in.readObject();
+        credentials = (String) in.readObject();
+
+        if (version > 1) {
+            timeout = in.readLong();
+            logout = in.readBoolean();
+        }
+    }
+
+    /**
+     * Changes to this method must observe the optional {@link #metaData} version
+     */
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        // write out the version of the serialized data for future use
+        out.writeByte(2);
+
+        out.writeObject(realm);
+        out.writeObject(username);
+        out.writeObject(credentials);
+        out.writeLong(timeout);
+        out.writeBoolean(logout);
+    }
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(50);
+        sb.append(null != realm ? realm : "Unknown realm").append(':');
+        sb.append(null != username ? username : "Unknown user").append(':');
+        sb.append(null != credentials ? credentials : "Unknown credentials").append(':');
+        sb.append(timeout);
+        return sb.toString();
+    }
+}
+