You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2016/08/18 16:00:26 UTC

tomee git commit: TOMEE-1908 allow JCA connectors to cast to their connection impl

Repository: tomee
Updated Branches:
  refs/heads/master 8444159f5 -> 64ca1f3e9


TOMEE-1908 allow JCA connectors to cast to their connection impl


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

Branch: refs/heads/master
Commit: 64ca1f3e9f7965d35e7a70a06ae60441d026c9a1
Parents: 8444159
Author: Romain manni-Bucau <rm...@gmail.com>
Authored: Thu Aug 18 18:00:13 2016 +0200
Committer: Romain manni-Bucau <rm...@gmail.com>
Committed: Thu Aug 18 18:00:13 2016 +0200

----------------------------------------------------------------------
 .../apache/openejb/dyni/DynamicSubclass.java    |  13 +
 .../openejb/resource/AutoConnectionTracker.java |  40 ++-
 .../util/proxy/DynamicProxyImplFactory.java     |   6 +-
 .../openejb/config/ConnectorProxyTest.java      | 292 +++++++++++++++++++
 4 files changed, 349 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/64ca1f3e/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java b/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
index 64b4a0a..5c27658 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
@@ -33,6 +33,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -88,6 +89,18 @@ public class DynamicSubclass implements Opcodes {
         }
     }
 
+    public static void setHandler(final Object instance, final InvocationHandler handler) {
+        try {
+            final Field thisHandler = instance.getClass().getDeclaredField("this$handler");
+            if (!thisHandler.isAccessible()) {
+                thisHandler.setAccessible(true);
+            }
+            thisHandler.set(instance, handler);
+        } catch (final NoSuchFieldException | IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
     private static byte[] generateBytes(final Class<?> classToProxy) throws ProxyGenerationException {
 
         final Map<String, MethodVisitor> visitors = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/tomee/blob/64ca1f3e/container/openejb-core/src/main/java/org/apache/openejb/resource/AutoConnectionTracker.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/AutoConnectionTracker.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/AutoConnectionTracker.java
index 0cc5b27..b7444de 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/resource/AutoConnectionTracker.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/AutoConnectionTracker.java
@@ -22,6 +22,7 @@ import org.apache.geronimo.connector.outbound.ConnectionReturnAction;
 import org.apache.geronimo.connector.outbound.ConnectionTrackingInterceptor;
 import org.apache.geronimo.connector.outbound.ManagedConnectionInfo;
 import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTracker;
+import org.apache.openejb.dyni.DynamicSubclass;
 import org.apache.openejb.util.LogCategory;
 import org.apache.openejb.util.Logger;
 
@@ -40,6 +41,7 @@ import java.util.concurrent.ConcurrentMap;
 public class AutoConnectionTracker implements ConnectionTracker {
     private final ConcurrentMap<ManagedConnectionInfo, ProxyPhantomReference> references = new ConcurrentHashMap<ManagedConnectionInfo, ProxyPhantomReference>();
     private final ReferenceQueue referenceQueue = new ReferenceQueue();
+    private final ConcurrentMap<Class<?>, Class<?>> proxies = new ConcurrentHashMap<>();
 
     public Set<ManagedConnectionInfo> connections() {
         return references.keySet();
@@ -105,7 +107,7 @@ public class AutoConnectionTracker implements ConnectionTracker {
         try {
             final Object handle = connectionInfo.getConnectionHandle();
             final ConnectionInvocationHandler invocationHandler = new ConnectionInvocationHandler(handle);
-            final Object proxy = Proxy.newProxyInstance(handle.getClass().getClassLoader(), handle.getClass().getInterfaces(), invocationHandler);
+            final Object proxy = newProxy(handle, invocationHandler);
             connectionInfo.setConnectionProxy(proxy);
             final ProxyPhantomReference reference = new ProxyPhantomReference(interceptor, connectionInfo.getManagedConnectionInfo(), invocationHandler, referenceQueue);
             references.put(connectionInfo.getManagedConnectionInfo(), reference);
@@ -114,6 +116,42 @@ public class AutoConnectionTracker implements ConnectionTracker {
         }
     }
 
+    private Object newProxy(final Object handle, final InvocationHandler invocationHandler) {
+        ClassLoader loader = handle.getClass().getClassLoader();
+        if (loader == null) {
+            loader = ClassLoader.getSystemClassLoader();
+        }
+        if (!Proxy.isProxyClass(handle.getClass())) {
+            try {
+                handle.getClass().getConstructor(); // if not let's the user reuse the impl-ed interfaces
+                try {
+                    final Object proxy = getProxy(handle.getClass(), loader).newInstance();
+                    DynamicSubclass.setHandler(proxy, invocationHandler);
+                    return proxy;
+                } catch (final InstantiationException | IllegalAccessException e) {
+                    throw new IllegalStateException(e);
+                }
+            } catch (final NoSuchMethodException e1) {
+                // no-op
+            }
+        }
+        return Proxy.newProxyInstance(loader, handle.getClass().getInterfaces(), invocationHandler);
+    }
+
+    private Class<?> getProxy(final Class<?> aClass, final ClassLoader loader) {
+        Class<?> found = proxies.get(aClass);
+        if (found == null) {
+            synchronized (this) {
+                found = proxies.get(aClass);
+                if (found == null) {
+                    proxies.put(aClass, DynamicSubclass.createSubclass(aClass, loader));
+                    found = proxies.get(aClass);
+                }
+            }
+        }
+        return found;
+    }
+
     public static class ConnectionInvocationHandler implements InvocationHandler {
         private final Object handle;
 

http://git-wip-us.apache.org/repos/asf/tomee/blob/64ca1f3e/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/DynamicProxyImplFactory.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/DynamicProxyImplFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/DynamicProxyImplFactory.java
index ea403f9..fed4c64 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/DynamicProxyImplFactory.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/DynamicProxyImplFactory.java
@@ -60,8 +60,12 @@ public class DynamicProxyImplFactory {
             QueryProxy.class.cast(invocationHandler).setEntityManager(em);
         }
 
+        return newProxy(context.getBeanClass(), invocationHandler);
+    }
+
+    public static Object newProxy(final Class<?> type, final InvocationHandler invocationHandler) {
         try {
-            return ProxyManager.newProxyInstance(context.getBeanClass(), new Handler(invocationHandler));
+            return ProxyManager.newProxyInstance(type, new Handler(invocationHandler));
         } catch (final IllegalAccessException e) {
             throw new OpenEJBRuntimeException("illegal access", e);
         }

http://git-wip-us.apache.org/repos/asf/tomee/blob/64ca1f3e/container/openejb-core/src/test/java/org/apache/openejb/config/ConnectorProxyTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/config/ConnectorProxyTest.java b/container/openejb-core/src/test/java/org/apache/openejb/config/ConnectorProxyTest.java
new file mode 100644
index 0000000..5db51c6
--- /dev/null
+++ b/container/openejb-core/src/test/java/org/apache/openejb/config/ConnectorProxyTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.config;
+
+import org.apache.openejb.jee.ConnectionDefinition;
+import org.apache.openejb.jee.Connector;
+import org.apache.openejb.jee.OutboundResourceAdapter;
+import org.apache.openejb.jee.ResourceAdapter;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.ContainerSystem;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testing.SimpleLog;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionMetaData;
+import javax.resource.cci.ConnectionSpec;
+import javax.resource.cci.Interaction;
+import javax.resource.cci.RecordFactory;
+import javax.resource.cci.ResourceAdapterMetaData;
+import javax.resource.cci.ResultSetInfo;
+import javax.resource.spi.ActivationSpec;
+import javax.resource.spi.BootstrapContext;
+import javax.resource.spi.ConnectionEventListener;
+import javax.resource.spi.ConnectionManager;
+import javax.resource.spi.ConnectionRequestInfo;
+import javax.resource.spi.LocalTransaction;
+import javax.resource.spi.ManagedConnection;
+import javax.resource.spi.ManagedConnectionFactory;
+import javax.resource.spi.ManagedConnectionMetaData;
+import javax.resource.spi.ResourceAdapterInternalException;
+import javax.resource.spi.endpoint.MessageEndpointFactory;
+import javax.security.auth.Subject;
+import javax.transaction.xa.XAResource;
+import java.io.PrintWriter;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+// dev note: the RA impl is very suspicious but it tests the fact we subclass connections
+@SimpleLog
+@RunWith(ApplicationComposer.class)
+public class ConnectorProxyTest {
+    @Module
+    public Connector connector() {
+        final ConnectionDefinition connectionDefinition = new ConnectionDefinition();
+        connectionDefinition.setId("cf");
+        connectionDefinition.setConnectionImplClass(MyCon.class.getName());
+        connectionDefinition.setConnectionInterface(MyConAPI.class.getName());
+        connectionDefinition.setConnectionFactoryImplClass(MyMcf.class.getName());
+        connectionDefinition.setConnectionFactoryInterface(ConnectionFactory.class.getName());
+        connectionDefinition.setManagedConnectionFactoryClass(MyMcf.class.getName());
+
+        final OutboundResourceAdapter out = new OutboundResourceAdapter();
+        out.getConnectionDefinition().add(connectionDefinition);
+
+        final ResourceAdapter ra = new ResourceAdapter();
+        ra.setResourceAdapterClass(MyRa.class.getName());
+        ra.setOutboundResourceAdapter(out);
+
+        final Connector connector = new Connector();
+        connector.setVersion("1.7");
+        connector.setResourceAdapter(ra);
+        return connector;
+    }
+
+    @Test
+    public void run() throws NamingException, ResourceException {
+        final MyCf jndi = MyCf.class.cast(SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext().lookup("openejb:Resource/cf"));
+        assertNotNull(jndi);
+
+        final Connection connection = jndi.getConnection();
+        assertTrue(MyConAPI.class.isInstance(connection));
+        assertTrue(MyCon.class.isInstance(connection));
+
+        final MyCon myCon = MyCon.class.cast(connection);
+        assertEquals("yes", myCon.specific());
+    }
+
+    public static class MyRa implements javax.resource.spi.ResourceAdapter {
+        @Override
+        public void start(final BootstrapContext ctx) throws ResourceAdapterInternalException {
+            // no-op
+        }
+
+        @Override
+        public void stop() {
+            // no-op
+        }
+
+        @Override
+        public void endpointActivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec) throws ResourceException {
+            // no-op
+        }
+
+        @Override
+        public void endpointDeactivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec) {
+            // no-op
+        }
+
+        @Override
+        public XAResource[] getXAResources(final ActivationSpec[] specs) throws ResourceException {
+            return new XAResource[0];
+        }
+    }
+
+    public static class MyCf implements ConnectionFactory {
+        private final ConnectionManager mgr;
+        private final ManagedConnectionFactory mcf;
+
+        public MyCf(final MyMcf myMcf, final ConnectionManager cxManager) {
+            this.mcf = myMcf;
+            this.mgr = cxManager;
+        }
+
+        @Override
+        public Connection getConnection() throws ResourceException {
+            return MyCon.class/*impl, this is what we want to test*/.cast(mgr.allocateConnection(mcf, new ConnectionRequestInfo() {
+            }));
+        }
+
+        @Override
+        public Connection getConnection(ConnectionSpec properties) throws ResourceException {
+            return getConnection();
+        }
+
+        @Override
+        public RecordFactory getRecordFactory() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public ResourceAdapterMetaData getMetaData() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public void setReference(Reference reference) {
+
+        }
+
+        @Override
+        public Reference getReference() throws NamingException {
+            return null;
+        }
+    }
+
+    public static class MyMcf implements ManagedConnectionFactory {
+        @Override
+        public Object createConnectionFactory(final ConnectionManager cxManager) throws ResourceException {
+            return new MyCf(this, cxManager);
+        }
+
+        @Override
+        public Object createConnectionFactory() throws ResourceException {
+            return new MyCf(this, null);
+        }
+
+        @Override
+        public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException {
+            return new MyMC();
+        }
+
+        @Override
+        public ManagedConnection matchManagedConnections(Set connectionSet, Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public void setLogWriter(PrintWriter out) throws ResourceException {
+
+        }
+
+        @Override
+        public PrintWriter getLogWriter() throws ResourceException {
+            return null;
+        }
+    }
+
+    public interface MyConAPI extends Connection {
+    }
+
+    public static class MyCon implements MyConAPI {
+        public String specific() {
+            return "yes";
+        }
+
+        @Override
+        public Interaction createInteraction() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public javax.resource.cci.LocalTransaction getLocalTransaction() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public ConnectionMetaData getMetaData() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public ResultSetInfo getResultSetInfo() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public void close() throws ResourceException {
+
+        }
+    }
+
+    public static class MyMC implements ManagedConnection {
+        @Override
+        public Object getConnection(final Subject subject, final ConnectionRequestInfo cxRequestInfo) throws ResourceException {
+            return new MyCon();
+        }
+
+        @Override
+        public void destroy() throws ResourceException {
+
+        }
+
+        @Override
+        public void cleanup() throws ResourceException {
+
+        }
+
+        @Override
+        public void associateConnection(Object connection) throws ResourceException {
+
+        }
+
+        @Override
+        public void addConnectionEventListener(ConnectionEventListener listener) {
+
+        }
+
+        @Override
+        public void removeConnectionEventListener(ConnectionEventListener listener) {
+
+        }
+
+        @Override
+        public XAResource getXAResource() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public LocalTransaction getLocalTransaction() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public ManagedConnectionMetaData getMetaData() throws ResourceException {
+            return null;
+        }
+
+        @Override
+        public void setLogWriter(PrintWriter out) throws ResourceException {
+
+        }
+
+        @Override
+        public PrintWriter getLogWriter() throws ResourceException {
+            return null;
+        }
+    }
+}