Repository: tomee Updated Branches: refs/heads/master 4983a0d45 -> 9fa28a270
TOMEE-2018 connectionFactoryLookup and destinationLookup not supported for mdbs Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/9fa28a27 Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/9fa28a27 Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/9fa28a27 Branch: refs/heads/master Commit: 9fa28a270008e8ff717cb6f750683a803f6d72dd Parents: 4983a0d Author: rmannibucau <[email protected]> Authored: Wed Mar 1 19:01:03 2017 +0100 Committer: rmannibucau <[email protected]> Committed: Wed Mar 1 19:01:03 2017 +0100 ---------------------------------------------------------------------- .../org/apache/openejb/config/AutoConfig.java | 12 ++ .../apache/openejb/core/mdb/MdbContainer.java | 2 + .../activemq/ActiveMQResourceAdapter.java | 74 ++++++++++ .../activemq/TomEEMessageActivationSpec.java | 31 ++++ .../activemq/jms2/cdi/JMS2CDIExtension.java | 28 +++- .../openejb/util/reflection/Reflections.java | 9 +- .../META-INF/org.apache.openejb/service-jar.xml | 2 +- .../apache/openejb/activemq/MDBSpecTest.java | 142 +++++++++++++++++++ 8 files changed, 292 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java b/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java index 0653f4d..5639b36 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java @@ -427,6 +427,10 @@ public class AutoConfig implements DynamicDeployer, JndiConstants { destination = properties.getProperty("destination"); } + if (destination == null) { // EE 7/EJB 3.2 + destination = properties.getProperty("destinationLookup"); + } + // destination // String destination = properties.getProperty("destination", properties.getProperty("destinationName")); if (destination == null) { @@ -2007,6 +2011,14 @@ public class AutoConfig implements DynamicDeployer, JndiConstants { return id; } + // app resources + if (appResources.appId != null && !appResources.appId.isEmpty() && resourceId.startsWith(appResources.appId + '/')) { + id = findResourceId(resourceId.substring(appResources.appId.length() + 1), type, required, appResources); + if (id != null) { + return id; + } + } + // throw an exception or log an error final String shortName = toShortName(resourceId); final String message = "No existing resource found while attempting to Auto-link unmapped resource-ref '" + http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java b/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java index 5223db2..389d228 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java @@ -216,6 +216,8 @@ public class MdbContainer implements RpcContainer { final Set<String> unusedProperties = new TreeSet<String>(objectRecipe.getUnsetProperties().keySet()); unusedProperties.remove("destination"); unusedProperties.remove("destinationType"); + unusedProperties.remove("destinationLookup"); + unusedProperties.remove("connectionFactoryLookup"); unusedProperties.remove("beanClass"); if (!unusedProperties.isEmpty()) { final String text = "No setter found for the activation spec properties: " + unusedProperties; http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/ActiveMQResourceAdapter.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/ActiveMQResourceAdapter.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/ActiveMQResourceAdapter.java index 6955035..dde1e2b 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/ActiveMQResourceAdapter.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/ActiveMQResourceAdapter.java @@ -17,20 +17,33 @@ package org.apache.openejb.resource.activemq; +import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.RedeliveryPolicy; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.ra.ActiveMQConnectionRequestInfo; +import org.apache.activemq.ra.ActiveMQManagedConnection; import org.apache.activemq.ra.MessageActivationSpec; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.resource.AutoConnectionTracker; import org.apache.openejb.resource.activemq.jms2.TomEEConnectionFactory; +import org.apache.openejb.resource.activemq.jms2.TomEEManagedConnectionProxy; +import org.apache.openejb.spi.ContainerSystem; import org.apache.openejb.util.Duration; import org.apache.openejb.util.LogCategory; import org.apache.openejb.util.Logger; import org.apache.openejb.util.URISupport; import org.apache.openejb.util.URLs; +import org.apache.openejb.util.reflection.Reflections; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.naming.NamingException; import javax.resource.spi.BootstrapContext; import javax.resource.spi.ResourceAdapterInternalException; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.URISyntaxException; import java.util.Collection; import java.util.Iterator; @@ -189,7 +202,68 @@ public class ActiveMQResourceAdapter extends org.apache.activemq.ra.ActiveMQReso } @Override + public ActiveMQConnection makeConnection(final MessageActivationSpec activationSpec) throws JMSException { + if (TomEEMessageActivationSpec.class.isInstance(activationSpec)) { + final TomEEMessageActivationSpec s = TomEEMessageActivationSpec.class.cast(activationSpec); + if (s.getConnectionFactoryLookup() != null) { + try { + final Object lookup = SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext() + .lookup("openejb:Resource/" + s.getConnectionFactoryLookup()); + if (!ActiveMQConnectionFactory.class.isInstance(lookup)) { + final org.apache.activemq.ra.ActiveMQConnectionFactory connectionFactory = org.apache.activemq.ra.ActiveMQConnectionFactory.class.cast(lookup); + Connection connection = connectionFactory.createConnection(); + if (Proxy.isProxyClass(connection.getClass())) { // not great, we should find a better want without bypassing ra layer + final InvocationHandler invocationHandler = Proxy.getInvocationHandler(connection); + if (AutoConnectionTracker.ConnectionInvocationHandler.class.isInstance(invocationHandler)) { + final Object handle = Reflections.get(invocationHandler, "handle"); + if (TomEEManagedConnectionProxy.class.isInstance(handle)) { + final ActiveMQManagedConnection c = ActiveMQManagedConnection.class.cast(Reflections.get(handle, "connection")); + final ActiveMQConnection physicalConnection = ActiveMQConnection.class.cast(Reflections.get(c, "physicalConnection")); + final RedeliveryPolicy redeliveryPolicy = activationSpec.redeliveryPolicy(); + if (redeliveryPolicy != null) { + physicalConnection.setRedeliveryPolicy(redeliveryPolicy); + } + return physicalConnection; + } + } + } + + /* + final RedeliveryPolicy redeliveryPolicy = activationSpec.redeliveryPolicy(); + if (redeliveryPolicy != null) { + physicalConnection.setRedeliveryPolicy(redeliveryPolicy); + } + */ + return null; + } + } catch (final ClassCastException cce) { + throw new java.lang.IllegalStateException(cce); + } catch (final NamingException e) { + throw new IllegalArgumentException(e); + } + } + } + return super.makeConnection(activationSpec); + } + + @Override protected ActiveMQConnectionFactory createConnectionFactory(final ActiveMQConnectionRequestInfo connectionRequestInfo, final MessageActivationSpec activationSpec) { + if (TomEEMessageActivationSpec.class.isInstance(activationSpec)) { + final TomEEMessageActivationSpec s = TomEEMessageActivationSpec.class.cast(activationSpec); + if (s.getConnectionFactoryLookup() != null) { + try { + final Object lookup = SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext() + .lookup("openejb:Resource/" + s.getConnectionFactoryLookup()); + if (ActiveMQConnectionFactory.class.isInstance(lookup)) { + return ActiveMQConnectionFactory.class.cast(lookup); + } + return ActiveMQConnectionFactory.class.cast(lookup); // already handled + } catch (final NamingException e) { + throw new IllegalArgumentException(""); + } + } + } + final ActiveMQConnectionFactory factory = new TomEEConnectionFactory(); connectionRequestInfo.configure(factory, activationSpec); return factory; http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/TomEEMessageActivationSpec.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/TomEEMessageActivationSpec.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/TomEEMessageActivationSpec.java new file mode 100644 index 0000000..9f30911 --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/TomEEMessageActivationSpec.java @@ -0,0 +1,31 @@ +/* + * 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.resource.activemq; + +import org.apache.activemq.ra.ActiveMQActivationSpec; + +public class TomEEMessageActivationSpec extends ActiveMQActivationSpec { + private String connectionFactoryLookup; + + public String getConnectionFactoryLookup() { + return connectionFactoryLookup; + } + + public void setConnectionFactoryLookup(final String connectionFactoryLookup) { + this.connectionFactoryLookup = connectionFactoryLookup; + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/cdi/JMS2CDIExtension.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/cdi/JMS2CDIExtension.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/cdi/JMS2CDIExtension.java index a8aef08..98ef326 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/cdi/JMS2CDIExtension.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/activemq/jms2/cdi/JMS2CDIExtension.java @@ -86,7 +86,7 @@ public class JMS2CDIExtension implements Extension { final JMSPasswordCredential credential = annotated.getAnnotation(JMSPasswordCredential.class); final String jndi = "openejb:Resource/" + - (jmsConnectionFactory == null ? findAnyConnectionFactory() : jmsConnectionFactory.value()); + (jmsConnectionFactory == null ? findAnyConnectionFactory() : findMatchingConnectionFactory(jmsConnectionFactory.value())); return new Key( jndi, credential != null ? credential.userName() : null, @@ -94,6 +94,32 @@ public class JMS2CDIExtension implements Extension { sessionMode != null ? sessionMode.value() : null); } + private String findMatchingConnectionFactory(final String value) { + final OpenEjbConfiguration component = SystemInstance.get().getComponent(OpenEjbConfiguration.class); + if (component != null && component.facilities != null) { + for (final ResourceInfo ri : component.facilities.resources) { + if (!ri.types.contains("javax.jms.ConnectionFactory")) { + continue; + } + if (ri.id.equals(value)) { + return ri.id; + } + } + // try application ones + for (final ResourceInfo ri : component.facilities.resources) { + if (!ri.types.contains("javax.jms.ConnectionFactory")) { + continue; + } + if (ri.id.endsWith(value)) { + return ri.id; + } + } + } + // something is wrong, just fail + throw new IllegalArgumentException( + "No connection factory found, either use @JMSConnectionFactory JMSContext or define a connection factory"); + } + private String findAnyConnectionFactory() { final OpenEjbConfiguration component = SystemInstance.get().getComponent(OpenEjbConfiguration.class); if (component != null && component.facilities != null) { http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/java/org/apache/openejb/util/reflection/Reflections.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/util/reflection/Reflections.java b/container/openejb-core/src/main/java/org/apache/openejb/util/reflection/Reflections.java index 250749b..1d0d065 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/util/reflection/Reflections.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/util/reflection/Reflections.java @@ -108,13 +108,10 @@ public final class Reflections { while (clazz != null) { try { final Field f = clazz.getDeclaredField(field); - final boolean acc = f.isAccessible(); - f.setAccessible(true); - try { - return f.get(instance); - } finally { - f.setAccessible(acc); + if (!f.isAccessible()) { + f.setAccessible(true); } + return f.get(instance); } catch (final NoSuchFieldException nsfe) { // no-op } catch (final Exception e) { http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml index 1df40e3..36eb8e0 100644 --- a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml +++ b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml @@ -482,7 +482,7 @@ # Specifies the activation spec class - ActivationSpecClass org.apache.activemq.ra.ActiveMQActivationSpec + ActivationSpecClass org.apache.openejb.resource.activemq.TomEEMessageActivationSpec # Specifies the maximum number of bean instances that are # allowed to exist for each MDB deployment. http://git-wip-us.apache.org/repos/asf/tomee/blob/9fa28a27/container/openejb-core/src/test/java/org/apache/openejb/activemq/MDBSpecTest.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/test/java/org/apache/openejb/activemq/MDBSpecTest.java b/container/openejb-core/src/test/java/org/apache/openejb/activemq/MDBSpecTest.java new file mode 100644 index 0000000..c0d7876 --- /dev/null +++ b/container/openejb-core/src/test/java/org/apache/openejb/activemq/MDBSpecTest.java @@ -0,0 +1,142 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.activemq; + +import org.apache.activemq.ra.ActiveMQConnectionRequestInfo; +import org.apache.activemq.ra.MessageActivationSpec; +import org.apache.openejb.junit.ApplicationComposer; +import org.apache.openejb.resource.activemq.ActiveMQResourceAdapter; +import org.apache.openejb.resource.activemq.TomEEMessageActivationSpec; +import org.apache.openejb.testing.Classes; +import org.apache.openejb.testing.Configuration; +import org.apache.openejb.testing.SimpleLog; +import org.apache.openejb.testng.PropertiesBuilder; +import org.apache.openejb.util.reflection.Reflections; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.annotation.Resource; +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.inject.Inject; +import javax.jms.JMSConnectionFactory; +import javax.jms.JMSConnectionFactoryDefinition; +import javax.jms.JMSContext; +import javax.jms.JMSDestinationDefinition; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Queue; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; + +@SimpleLog +@RunWith(ApplicationComposer.class) +@Classes(cdi = true, innerClassesAsBean = true) +public class MDBSpecTest { + @Configuration + public Properties config() { + return new PropertiesBuilder() + // .p("openejb.offline", "true") // helpful in dev but not working cause of dynamic resources + + .p("amq", "new://Resource?type=ActiveMQResourceAdapter") + .p("amq.DataSource", "") + .p("amq.BrokerXmlConfig", "broker:(vm://localhost)") + + .p("mdbs", "new://Container?type=MESSAGE") + .p("mdbs.ResourceAdapter", "amq") + + .p("testcontainer", "new://Container?type=MANAGED") + + .build(); + } + + @Resource(name = "amq") + private ActiveMQResourceAdapter amq; + + @Resource(name = "jms/input") + private Queue destination; + + @Inject + @JMSConnectionFactory("jms/ConnectionFactory") + private JMSContext context; + + @Before + public void resetLatch() { + Listener.reset(); + } + + @Test + public void checkConfig() throws InterruptedException { + // first it works in term of communication + context.createProducer().send(destination, "hello"); + assertTrue(Listener.sync()); + + // then we should check we don't create a connection factory but use the config one + // Note: if you have time use a custom connection factory to have a better tracking, for now it should be good enough otherwise + } + + private Object createFactory(final ActiveMQResourceAdapter amq) { + return Reflections.invokeByReflection( + amq, + "createConnectionFactory", + new Class<?>[]{ActiveMQConnectionRequestInfo.class, MessageActivationSpec.class}, + new Object[]{null, new TomEEMessageActivationSpec() {{ + setConnectionFactoryLookup("jms/XAConnectionFactory"); + }}}); + } + + @JMSDestinationDefinition(name = "jms/input", destinationName = "jms/input", interfaceName = "javax.jms.Queue") + @JMSConnectionFactoryDefinition(name = "jms/ConnectionFactory", transactional = false) + @MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), + @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/input"), + @ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "jms/ConnectionFactory") + }) + public static class Listener implements MessageListener { + public static volatile CountDownLatch latch; + public static volatile boolean ok = false; + + @Override + public void onMessage(final Message message) { + try { + try { + final String body = message.getBody(String.class); + ok = "hello".equals(body); + } catch (final JMSException e) { + // no-op + } + } finally { + latch.countDown(); + } + } + + public static void reset() { + latch = new CountDownLatch(1); + ok = false; + } + + public static boolean sync() throws InterruptedException { + latch.await(1, TimeUnit.MINUTES); + return ok; + } + } +}
