Package org.apache.geronimo.connector.outbound.connectiontracking

Source Code of org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTrackingCoordinator$ConnectionInvocationHandler

/**
*  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.geronimo.connector.outbound.connectiontracking;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.resource.ResourceException;
import javax.resource.spi.DissociatableManagedConnection;

import org.apache.geronimo.connector.outbound.ConnectionInfo;
import org.apache.geronimo.connector.outbound.ConnectionReturnAction;
import org.apache.geronimo.connector.outbound.ConnectionTrackingInterceptor;
import org.apache.geronimo.connector.outbound.ManagedConnectionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* ConnectionTrackingCoordinator tracks connections that are in use by
* components such as EJB's.  The component must notify the ccm
* when a method enters and exits.  On entrance, the ccm will
* notify ConnectionManager stacks so the stack can make sure all
* connection handles left open from previous method calls are
* attached to ManagedConnections of the correct security context, and
* the ManagedConnections are enrolled in any current transaction.
* On exit, the ccm will notify ConnectionManager stacks of the handles
* left open, so they may be disassociated if appropriate.
* In addition, when a UserTransaction is started the ccm will notify
* ConnectionManager stacks so the existing ManagedConnections can be
* enrolled properly.
*
* @version $Rev: 704747 $ $Date: 2008-10-15 08:14:41 +0800 (Wed, 15 Oct 2008) $
*/
public class ConnectionTrackingCoordinator implements TrackedConnectionAssociator, ConnectionTracker {
    private static final Logger log = LoggerFactory.getLogger(ConnectionTrackingCoordinator.class.getName());

    private final boolean lazyConnect;
    private final ThreadLocal<ConnectorInstanceContext> currentInstanceContexts = new ThreadLocal<ConnectorInstanceContext>();
    private final ConcurrentMap<ConnectionInfo,Object> proxiesByConnectionInfo = new ConcurrentHashMap<ConnectionInfo,Object>();

    public ConnectionTrackingCoordinator() {
        this(false);
    }

    public ConnectionTrackingCoordinator(boolean lazyConnect) {
        this.lazyConnect = lazyConnect;
    }

    public boolean isLazyConnect() {
        return lazyConnect;
    }

    public ConnectorInstanceContext enter(ConnectorInstanceContext newContext) throws ResourceException {
        ConnectorInstanceContext oldContext = currentInstanceContexts.get();
        currentInstanceContexts.set(newContext);
        associateConnections(newContext);
        return oldContext;
    }

    private void associateConnections(ConnectorInstanceContext context) throws ResourceException {
        Map<ConnectionTrackingInterceptor, Set<ConnectionInfo>> connectionManagerToManagedConnectionInfoMap = context.getConnectionManagerMap();
        for (Map.Entry<ConnectionTrackingInterceptor, Set<ConnectionInfo>> entry : connectionManagerToManagedConnectionInfoMap.entrySet()) {
            ConnectionTrackingInterceptor mcci = entry.getKey();
            Set<ConnectionInfo> connections = entry.getValue();
            mcci.enter(connections);
        }
    }

    public void newTransaction() throws ResourceException {
        ConnectorInstanceContext currentContext = currentInstanceContexts.get();
        if (currentContext == null) {
            return;
        }
        associateConnections(currentContext);
    }

    public void exit(ConnectorInstanceContext oldContext) throws ResourceException {
        ConnectorInstanceContext currentContext = currentInstanceContexts.get();
        try {
            // for each connection type opened in this componet
            Map<ConnectionTrackingInterceptor, Set<ConnectionInfo>> resources = currentContext.getConnectionManagerMap();
            for (Iterator<Map.Entry<ConnectionTrackingInterceptor, Set<ConnectionInfo>>> iterator = resources.entrySet().iterator(); iterator.hasNext();) {
                Map.Entry<ConnectionTrackingInterceptor, Set<ConnectionInfo>> entry = iterator.next();
                ConnectionTrackingInterceptor mcci = entry.getKey();
                Set<ConnectionInfo> connections = entry.getValue();

                // release proxy connections
                if (lazyConnect) {
                    for (ConnectionInfo connectionInfo : connections) {
                        releaseProxyConnection(connectionInfo);
                    }
                }

                // use connection interceptor to dissociate connections that support disassociation
                mcci.exit(connections);

                // if no connection remain clear context... we could support automatic commit, rollback or exception here
                if (connections.isEmpty()) {
                    iterator.remove();
                }
            }
        } finally {
            // when lazy we do not need or want to track open connections... they will automatically reconnect
            if (lazyConnect) {
                currentContext.getConnectionManagerMap().clear();
            }
            currentInstanceContexts.set(oldContext);
        }
    }

    /**
     * A new connection (handle) has been obtained.  If we are within a component context, store the connection handle
     * so we can disassociate connections that support disassociation on exit.
     * @param connectionTrackingInterceptor our interceptor in the connection manager which is used to disassociate the connections
     * @param connectionInfo the connection that was obtained
     * @param reassociate
     */
    public void handleObtained(ConnectionTrackingInterceptor connectionTrackingInterceptor,
            ConnectionInfo connectionInfo,
            boolean reassociate) throws ResourceException {

        ConnectorInstanceContext currentContext = currentInstanceContexts.get();
        if (currentContext == null) {
            return;
        }

        Map<ConnectionTrackingInterceptor, Set<ConnectionInfo>> resources = currentContext.getConnectionManagerMap();
        Set<ConnectionInfo> infos = resources.get(connectionTrackingInterceptor);
        if (infos == null) {
            infos = new HashSet<ConnectionInfo>();
            resources.put(connectionTrackingInterceptor, infos);
        }

        infos.add(connectionInfo);

        // if lazyConnect, we must proxy so we know when to connect the proxy
        if (!reassociate && lazyConnect) {
            proxyConnection(connectionTrackingInterceptor, connectionInfo);
        }
    }

    /**
     * A connection (handle) has been released or destroyed.  If we are within a component context, remove the connection
     * handle from the context.
     * @param connectionTrackingInterceptor our interceptor in the connection manager
     * @param connectionInfo the connection that was released
     * @param connectionReturnAction
     */
    public void handleReleased(ConnectionTrackingInterceptor connectionTrackingInterceptor,
            ConnectionInfo connectionInfo,
            ConnectionReturnAction connectionReturnAction) {

        ConnectorInstanceContext currentContext = currentInstanceContexts.get();
        if (currentContext == null) {
            return;
        }

        Map<ConnectionTrackingInterceptor, Set<ConnectionInfo>> resources = currentContext.getConnectionManagerMap();
        Set<ConnectionInfo> infos = resources.get(connectionTrackingInterceptor);
        if (infos != null) {
            if (connectionInfo.getConnectionHandle() == null) {
                //destroy was called as a result of an error
                ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo();
                Collection<ConnectionInfo> toRemove = mci.getConnectionInfos();
                infos.removeAll(toRemove);
            } else {
                infos.remove(connectionInfo);
            }
        } else {
            if ( log.isTraceEnabled()) {
                 log.trace("No infos found for handle " + connectionInfo.getConnectionHandle() +
                         " for MCI: " + connectionInfo.getManagedConnectionInfo() +
                         " for MC: " + connectionInfo.getManagedConnectionInfo().getManagedConnection() +
                         " for CTI: " + connectionTrackingInterceptor, new Exception("Stack Trace"));
            }
        }

        // NOTE: This method is also called by DissociatableManagedConnection when a connection has been
        // dissociated in addition to the normal connection closed notification, but this is not a problem
        // because DissociatableManagedConnection are not proied so this method will have no effect
        closeProxyConnection(connectionInfo);
    }

    /**
     * If we are within a component context, before a connection is obtained, set the connection unshareable and
     * applicationManagedSecurity properties so the correct connection type is obtained.
     * @param connectionInfo the connection to be obtained
     * @param key the unique id of the connection manager
     */
    public void setEnvironment(ConnectionInfo connectionInfo, String key) {
        ConnectorInstanceContext currentContext = currentInstanceContexts.get();
        if (currentContext != null) {
            // is this resource unshareable in this component context
            Set<String> unshareableResources = currentContext.getUnshareableResources();
            boolean unshareable = unshareableResources.contains(key);
            connectionInfo.setUnshareable(unshareable);

            // does this resource use application managed security in this component context
            Set<String> applicationManagedSecurityResources = currentContext.getApplicationManagedSecurityResources();
            boolean applicationManagedSecurity = applicationManagedSecurityResources.contains(key);
            connectionInfo.setApplicationManagedSecurity(applicationManagedSecurity);
        }
    }

    private void proxyConnection(ConnectionTrackingInterceptor connectionTrackingInterceptor, ConnectionInfo connectionInfo) throws ResourceException {
        // if this connection already has a proxy no need to create another
        if (connectionInfo.getConnectionProxy() != null) return;

        // DissociatableManagedConnection do not need to be proxied
        if (connectionInfo.getManagedConnectionInfo().getManagedConnection() instanceof DissociatableManagedConnection) {
            return;
        }

        try {
            Object handle = connectionInfo.getConnectionHandle();
            ConnectionInvocationHandler invocationHandler = new ConnectionInvocationHandler(connectionTrackingInterceptor, connectionInfo, handle);
            Object proxy = Proxy.newProxyInstance(getClassLoader(handle), handle.getClass().getInterfaces(), invocationHandler);

            // add it to our map... if the map already has a proxy for this connection, use the existing one
            Object existingProxy = proxiesByConnectionInfo.putIfAbsent(connectionInfo, proxy);
            if (existingProxy != null) proxy = existingProxy;

            connectionInfo.setConnectionProxy(proxy);
        } catch (Throwable e) {
            throw new ResourceException("Unable to construct connection proxy", e);
        }
    }

    private void releaseProxyConnection(ConnectionInfo connectionInfo) {
        ConnectionInvocationHandler invocationHandler = getConnectionInvocationHandler(connectionInfo);
        if (invocationHandler != null) {
            invocationHandler.releaseHandle();
        }
    }

    private void closeProxyConnection(ConnectionInfo connectionInfo) {
        ConnectionInvocationHandler invocationHandler = getConnectionInvocationHandler(connectionInfo);
        if (invocationHandler != null) {
            invocationHandler.close();
            proxiesByConnectionInfo.remove(connectionInfo);
            connectionInfo.setConnectionProxy(null);
        }
    }

    // Favor the thread context class loader for proxy construction
    private ClassLoader getClassLoader(Object handle) {
        ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();
        if (threadClassLoader != null) {
            return threadClassLoader;
        }
        return handle.getClass().getClassLoader();
    }

    private ConnectionInvocationHandler getConnectionInvocationHandler(ConnectionInfo connectionInfo) {
        Object proxy = connectionInfo.getConnectionProxy();
        if (proxy == null) {
            proxy = proxiesByConnectionInfo.get(connectionInfo);
        }

        // no proxy or proxy already destroyed
        if (proxy == null) return null;

        if (Proxy.isProxyClass(proxy.getClass())) {
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
            if (invocationHandler instanceof ConnectionInvocationHandler) {
                return (ConnectionInvocationHandler) invocationHandler;
            }
        }
        return null;
    }

    public static class ConnectionInvocationHandler implements InvocationHandler {
        private ConnectionTrackingInterceptor connectionTrackingInterceptor;
        private ConnectionInfo connectionInfo;
        private final Object handle;
        private boolean released = false;

        public ConnectionInvocationHandler(ConnectionTrackingInterceptor connectionTrackingInterceptor, ConnectionInfo connectionInfo, Object handle) {
            this.connectionTrackingInterceptor = connectionTrackingInterceptor;
            this.connectionInfo = connectionInfo;
            this.handle = handle;
        }

        public Object invoke(Object object, Method method, Object[] args) throws Throwable {
            Object handle;
            if (method.getDeclaringClass() == Object.class) {
                if (method.getName().equals("finalize")) {
                    // ignore the handle will get called if it implemented the method
                    return null;
                }
                if (method.getName().equals("clone")) {
                    throw new CloneNotSupportedException();
                }
                // for equals, hashCode and toString don't activate handle
                synchronized (this) {
                    handle = this.handle;
                }
            } else {
                handle = getHandle();
            }
           
            try {
                Object value = method.invoke(handle, args);
                return value;
            } catch (InvocationTargetException ite) {
                // catch InvocationTargetExceptions and turn them into the target exception (if there is one)
                Throwable t = ite.getTargetException();
                if (t != null) {
                    throw t;
                }
                throw ite;
            }

        }

        public synchronized boolean isReleased() {
            return released;
        }

        public synchronized void releaseHandle() {
            released = true;
        }

        public synchronized void close() {
            connectionTrackingInterceptor = null;
            connectionInfo = null;
            released = true;
        }

        public synchronized Object getHandle() {
            if (connectionTrackingInterceptor == null) {
                // connection has been closed... send invocations directly to the handle
                // which will throw an exception or in some clases like JDBC connection.close()
                // ignore the invocation
                return handle;
            }

            if (released) {
                try {
                    connectionTrackingInterceptor.reassociateConnection(connectionInfo);
                } catch (ResourceException e) {
                    throw (IllegalStateException) new IllegalStateException("Could not obtain a physical connection").initCause(e);
                }
                released = false;
            }
            return handle;
        }
    }
}
TOP

Related Classes of org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTrackingCoordinator$ConnectionInvocationHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.