From 02cedbf8da89e3cf0f5b1e7834dc5abc94827336 Mon Sep 17 00:00:00 2001 From: Howard Gao Date: Fri, 23 Oct 2015 21:46:50 +0800 Subject: [PATCH] ARTEMIS-276 JMSBridge should be TCCL aware --- .../jms/bridge/impl/JMSBridgeImpl.java | 28 ++++- .../jms/bridge/impl/JMSBridgeImplTest.java | 114 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java index 8a22bb1793..f3bd337c3d 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java @@ -58,6 +58,8 @@ import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.TransactionRolledbackException; import javax.transaction.xa.XAResource; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; @@ -67,6 +69,7 @@ import java.util.Map.Entry; import java.util.ServiceLoader; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; public final class JMSBridgeImpl implements JMSBridge { @@ -176,6 +179,8 @@ public final class JMSBridgeImpl implements JMSBridge { private ActiveMQRegistry registry; + private ClassLoader moduleTccl; + /* * Constructor for MBean */ @@ -319,6 +324,12 @@ public final class JMSBridgeImpl implements JMSBridge { stopping = false; } + moduleTccl = AccessController.doPrivileged(new PrivilegedAction() { + public ClassLoader run() { + return Thread.currentThread().getContextClassLoader(); + } + }); + locateRecoveryRegistry(); if (started) { @@ -1545,7 +1556,22 @@ public final class JMSBridgeImpl implements JMSBridge { * and 1 for the eventual failureHandler) */ private ExecutorService createExecutor() { - return Executors.newFixedThreadPool(3); + ExecutorService service = Executors.newFixedThreadPool(3, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + final Thread thr = new Thread(r); + if (moduleTccl != null) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + thr.setContextClassLoader(moduleTccl); + return null; + } + }); + } + return thr; + } + }); + return service; } // Inner classes --------------------------------------------------------------- diff --git a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java index 9e9fd09267..2ff8886ef9 100644 --- a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java +++ b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java @@ -68,6 +68,7 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public class JMSBridgeImplTest extends ActiveMQTestBase { @@ -83,6 +84,8 @@ public class JMSBridgeImplTest extends ActiveMQTestBase { private JMSServerManager jmsServer; + private static final AtomicBoolean tcclClassFound = new AtomicBoolean(false); + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -148,6 +151,26 @@ public class JMSBridgeImplTest extends ActiveMQTestBase { return cf; } + private static ConnectionFactoryFactory newTCCLAwareConnectionFactoryFactory(final ConnectionFactory cf) { + return new ConnectionFactoryFactory() { + public ConnectionFactory createConnectionFactory() throws Exception { + loadATCCLClass(); + return cf; + } + + private void loadATCCLClass() { + ClassLoader tcclClassLoader = Thread.currentThread().getContextClassLoader(); + try { + tcclClassLoader.loadClass("com.class.only.visible.to.tccl.SomeClass"); + tcclClassFound.set(true); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + }; + } + // Constructors -------------------------------------------------- // Public -------------------------------------------------------- @@ -546,9 +569,80 @@ public class JMSBridgeImplTest extends ActiveMQTestBase { } + @Test + public void testStartWithSpecificTCCL() throws Exception { + MockContextClassLoader mockTccl = setMockTCCL(); + try { + final AtomicReference sourceConn = new AtomicReference(); + ActiveMQJMSConnectionFactory failingSourceCF = new ActiveMQJMSConnectionFactory(false, new TransportConfiguration(InVMConnectorFactory.class.getName())) { + private static final long serialVersionUID = -8866390811966688830L; + + @Override + public Connection createConnection() throws JMSException { + sourceConn.set(super.createConnection()); + return sourceConn.get(); + } + }; + // Note! We disable automatic reconnection on the session factory. The bridge needs to do the reconnection + failingSourceCF.setReconnectAttempts(0); + failingSourceCF.setBlockOnNonDurableSend(true); + failingSourceCF.setBlockOnDurableSend(true); + + ConnectionFactoryFactory sourceCFF = JMSBridgeImplTest.newTCCLAwareConnectionFactoryFactory(failingSourceCF); + ConnectionFactoryFactory targetCFF = JMSBridgeImplTest.newConnectionFactoryFactory(JMSBridgeImplTest.createConnectionFactory()); + DestinationFactory sourceDF = JMSBridgeImplTest.newDestinationFactory(ActiveMQJMSClient.createQueue(JMSBridgeImplTest.SOURCE)); + DestinationFactory targetDF = JMSBridgeImplTest.newDestinationFactory(ActiveMQJMSClient.createQueue(JMSBridgeImplTest.TARGET)); + TransactionManager tm = JMSBridgeImplTest.newTransactionManager(); + + JMSBridgeImpl bridge = new JMSBridgeImpl(); + Assert.assertNotNull(bridge); + + bridge.setSourceConnectionFactoryFactory(sourceCFF); + bridge.setSourceDestinationFactory(sourceDF); + bridge.setTargetConnectionFactoryFactory(targetCFF); + bridge.setTargetDestinationFactory(targetDF); + bridge.setFailureRetryInterval(10); + bridge.setMaxRetries(2); + bridge.setMaxBatchSize(1); + bridge.setMaxBatchTime(-1); + bridge.setTransactionManager(tm); + bridge.setQualityOfServiceMode(QualityOfServiceMode.AT_MOST_ONCE); + + Assert.assertFalse(bridge.isStarted()); + bridge.start(); + Assert.assertTrue(bridge.isStarted()); + + unsetMockTCCL(mockTccl); + tcclClassFound.set(false); + + sourceConn.get().getExceptionListener().onException(new JMSException("exception on the source")); + Thread.sleep(4 * bridge.getFailureRetryInterval()); + // reconnection must have succeeded + Assert.assertTrue(bridge.isStarted()); + + bridge.stop(); + Assert.assertFalse(bridge.isStarted()); + assertTrue(tcclClassFound.get()); + } + finally { + if (mockTccl != null) unsetMockTCCL(mockTccl); + } + } + + // Package protected --------------------------------------------- // Protected ----------------------------------------------------- + private static MockContextClassLoader setMockTCCL() { + ClassLoader parent = JMSBridgeImpl.class.getClassLoader(); + MockContextClassLoader tccl = new MockContextClassLoader(parent); + Thread.currentThread().setContextClassLoader(tccl); + return tccl; + } + + private static void unsetMockTCCL(MockContextClassLoader mockTccl) { + Thread.currentThread().setContextClassLoader(mockTccl.getOriginal()); + } @Override @Before @@ -633,5 +727,25 @@ public class JMSBridgeImplTest extends ActiveMQTestBase { // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- + private static class MockContextClassLoader extends ClassLoader { + private final ClassLoader original; + private final String knownClass = "com.class.only.visible.to.tccl.SomeClass"; + public MockContextClassLoader(ClassLoader parent) { + super(parent); + original = Thread.currentThread().getContextClassLoader(); + } + + public ClassLoader getOriginal() { + return original; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (knownClass.equals(name)) { + return null; + } + return super.findClass(name); + } + } }