001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.resource;
018:
019: import org.apache.geronimo.connector.outbound.ConnectionInfo;
020: import org.apache.geronimo.connector.outbound.ConnectionReturnAction;
021: import org.apache.geronimo.connector.outbound.ConnectionTrackingInterceptor;
022: import org.apache.geronimo.connector.outbound.ManagedConnectionInfo;
023: import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTracker;
024:
025: import javax.resource.ResourceException;
026: import javax.resource.spi.DissociatableManagedConnection;
027: import java.lang.ref.PhantomReference;
028: import java.lang.ref.ReferenceQueue;
029: import java.lang.reflect.InvocationHandler;
030: import java.lang.reflect.InvocationTargetException;
031: import java.lang.reflect.Method;
032: import java.lang.reflect.Proxy;
033: import java.util.concurrent.ConcurrentHashMap;
034: import java.util.concurrent.ConcurrentMap;
035:
036: public class AutoConnectionTracker implements ConnectionTracker {
037: private final ConcurrentMap<ManagedConnectionInfo, ProxyPhantomReference> references = new ConcurrentHashMap<ManagedConnectionInfo, ProxyPhantomReference>();
038: private final ReferenceQueue referenceQueue = new ReferenceQueue();
039:
040: /**
041: * Releases any managed connections held by a garbage collected connection proxy.
042: * @param connectionInfo the connection to be obtained
043: * @param key the unique id of the connection manager
044: */
045: public void setEnvironment(ConnectionInfo connectionInfo, String key) {
046: ProxyPhantomReference reference = (ProxyPhantomReference) referenceQueue
047: .poll();
048: while (reference != null) {
049: reference.clear();
050: references.remove(reference.managedConnectionInfo);
051:
052: ConnectionInfo released = new ConnectionInfo(
053: reference.managedConnectionInfo);
054: reference.interceptor.returnConnection(released,
055: ConnectionReturnAction.DESTROY);
056: reference = (ProxyPhantomReference) referenceQueue.poll();
057: }
058: }
059:
060: /**
061: * Proxies new connection handles so we can detect when they have been garbage collected.
062: *
063: * @param interceptor the interceptor used to release the managed connection when the handled is garbage collected.
064: * @param connectionInfo the connection that was obtained
065: * @param reassociate should always be false
066: */
067: public void handleObtained(
068: ConnectionTrackingInterceptor interceptor,
069: ConnectionInfo connectionInfo, boolean reassociate)
070: throws ResourceException {
071: if (!reassociate) {
072: proxyConnection(interceptor, connectionInfo);
073: }
074: }
075:
076: /**
077: * Removes the released collection from the garbage collection reference tracker, since this
078: * connection is being release via a normal close method.
079: *
080: * @param interceptor ignored
081: * @param connectionInfo the connection that was released
082: * @param action ignored
083: */
084: public void handleReleased(
085: ConnectionTrackingInterceptor interceptor,
086: ConnectionInfo connectionInfo, ConnectionReturnAction action) {
087: PhantomReference phantomReference = references
088: .remove(connectionInfo.getManagedConnectionInfo());
089: if (phantomReference != null) {
090: phantomReference.clear();
091: }
092: }
093:
094: private void proxyConnection(
095: ConnectionTrackingInterceptor interceptor,
096: ConnectionInfo connectionInfo) throws ResourceException {
097: // if this connection already has a proxy no need to create another
098: if (connectionInfo.getConnectionProxy() != null)
099: return;
100:
101: // DissociatableManagedConnection do not need to be proxied
102: if (connectionInfo.getManagedConnectionInfo()
103: .getManagedConnection() instanceof DissociatableManagedConnection) {
104: return;
105: }
106:
107: try {
108: Object handle = connectionInfo.getConnectionHandle();
109: ConnectionInvocationHandler invocationHandler = new ConnectionInvocationHandler(
110: handle);
111: Object proxy = Proxy.newProxyInstance(handle.getClass()
112: .getClassLoader(), handle.getClass()
113: .getInterfaces(), invocationHandler);
114: connectionInfo.setConnectionProxy(proxy);
115: ProxyPhantomReference reference = new ProxyPhantomReference(
116: interceptor, connectionInfo
117: .getManagedConnectionInfo(),
118: invocationHandler, referenceQueue);
119: references.put(connectionInfo.getManagedConnectionInfo(),
120: reference);
121: } catch (Throwable e) {
122: throw new ResourceException(
123: "Unable to construct connection proxy", e);
124: }
125: }
126:
127: public static class ConnectionInvocationHandler implements
128: InvocationHandler {
129: private final Object handle;
130:
131: public ConnectionInvocationHandler(Object handle) {
132: this .handle = handle;
133: }
134:
135: public Object invoke(Object object, Method method, Object[] args)
136: throws Throwable {
137: if (method.getDeclaringClass() == Object.class) {
138: if (method.getName().equals("finalize")) {
139: // ignore the handle will get called if it implemented the method
140: return null;
141: }
142: if (method.getName().equals("clone")) {
143: throw new CloneNotSupportedException();
144: }
145: }
146:
147: try {
148: Object value = method.invoke(handle, args);
149: return value;
150: } catch (InvocationTargetException ite) {
151: // catch InvocationTargetExceptions and turn them into the target exception (if there is one)
152: Throwable t = ite.getTargetException();
153: if (t != null) {
154: throw t;
155: }
156: throw ite;
157: }
158: }
159: }
160:
161: private static class ProxyPhantomReference extends
162: PhantomReference<ConnectionInvocationHandler> {
163: private ConnectionTrackingInterceptor interceptor;
164: private ManagedConnectionInfo managedConnectionInfo;
165:
166: @SuppressWarnings({"unchecked"})
167: public ProxyPhantomReference(
168: ConnectionTrackingInterceptor interceptor,
169: ManagedConnectionInfo managedConnectionInfo,
170: ConnectionInvocationHandler handler,
171: ReferenceQueue referenceQueue) {
172: super(handler, referenceQueue);
173: this.interceptor = interceptor;
174: this.managedConnectionInfo = managedConnectionInfo;
175: }
176: }
177: }
|