http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/transactional/src/main/java/org/apache/activemq/artemis/jms/example/TransactionalExample.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/transactional/src/main/java/org/apache/activemq/artemis/jms/example/TransactionalExample.java b/examples/broker-features/standard/transactional/src/main/java/org/apache/activemq/artemis/jms/example/TransactionalExample.java new file mode 100644 index 0000000..09c2a3d --- /dev/null +++ b/examples/broker-features/standard/transactional/src/main/java/org/apache/activemq/artemis/jms/example/TransactionalExample.java @@ -0,0 +1,126 @@ +/* + * 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.activemq.artemis.jms.example; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +/** + * A simple JMS example that sends and consume message transactionally. + */ +public class TransactionalExample { + + public static void main(final String[] args) throws Exception { + Connection connection = null; + InitialContext initialContext = null; + try { + // Step 1. Create an initial context to perform the JNDI lookup. + initialContext = new InitialContext(); + + // Step 2. Look-up the JMS topic + Queue queue = (Queue) initialContext.lookup("queue/exampleQueue"); + + // Step 3. Look-up the JMS connection factory + ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("ConnectionFactory"); + + // Step 4. Create a JMS connection + connection = cf.createConnection(); + + // Step 5. Start the connection + connection.start(); + + // Step 6. Create a transactional JMS session + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + + // Step 7. Create a JMS message producer + MessageProducer messageProducer = session.createProducer(queue); + + // Step 8. Create a message consumer + MessageConsumer messageConsumer = session.createConsumer(queue); + + // Step 9. Create 2 text messages + TextMessage message1 = session.createTextMessage("This is a text message1"); + TextMessage message2 = session.createTextMessage("This is a text message2"); + + // Step 10. Send the text messages to the queue + messageProducer.send(message1); + messageProducer.send(message2); + + System.out.println("Sent message: " + message1.getText()); + System.out.println("Sent message: " + message2.getText()); + + // Step 11. Receive the message, it will return null as the transaction is not committed. + TextMessage receivedMessage = (TextMessage) messageConsumer.receive(5000); + + System.out.println("Message received before send commit: " + receivedMessage); + + // Step 12. Commit the session + session.commit(); + + // Step 13. Receive the messages again + receivedMessage = (TextMessage) messageConsumer.receive(5000); + + System.out.println("Message received after send commit: " + receivedMessage.getText()); + + // Step 14. Roll back the session, this will cause the received message canceled and redelivered again. + session.rollback(); + + // Step 15. Receive the message again, we will get two messages + receivedMessage = (TextMessage) messageConsumer.receive(5000); + + System.out.println("Message1 received after receive rollback: " + receivedMessage.getText()); + + receivedMessage = (TextMessage) messageConsumer.receive(5000); + + System.out.println("Message2 received after receive rollback: " + receivedMessage.getText()); + + receivedMessage = (TextMessage) messageConsumer.receive(5000); + + System.out.println("Message3 received after receive rollback: " + receivedMessage); + + // Step 16. Commit the session + session.commit(); + + // Step 17. Receive the message again. Nothing should be received. + receivedMessage = (TextMessage) messageConsumer.receive(5000); + + if (receivedMessage != null) { + // This was not supposed to happen + throw new IllegalStateException("Message is not null."); + } + + System.out.println("Message received after receive commit: " + receivedMessage); + + } + finally { + if (connection != null) { + // Step 18. Be sure to close our JMS resources! + connection.close(); + } + if (initialContext != null) { + // Step 19. Also close initial context! + initialContext.close(); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/transactional/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/transactional/src/main/resources/jndi.properties b/examples/broker-features/standard/transactional/src/main/resources/jndi.properties new file mode 100644 index 0000000..93537c4 --- /dev/null +++ b/examples/broker-features/standard/transactional/src/main/resources/jndi.properties @@ -0,0 +1,20 @@ +# 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. + +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://localhost:61616 +queue.queue/exampleQueue=exampleQueue http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-heuristic/pom.xml ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-heuristic/pom.xml b/examples/broker-features/standard/xa-heuristic/pom.xml new file mode 100644 index 0000000..6f4025b --- /dev/null +++ b/examples/broker-features/standard/xa-heuristic/pom.xml @@ -0,0 +1,111 @@ +<?xml version='1.0'?> +<!-- +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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>jms-examples</artifactId> + <version>1.0.1-SNAPSHOT</version> + </parent> + + <artifactId>xa-heuristic</artifactId> + <packaging>jar</packaging> + <name>ActiveMQ Artemis JMS XAHeuristicExample Example</name> + + <properties> + <activemq.basedir>${project.basedir}/../../../..</activemq.basedir> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-jms-client</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-maven-plugin</artifactId> + <executions> + <execution> + <id>create</id> + <goals> + <goal>create</goal> + </goals> + <configuration> + <javaOptions>-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=3001 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false + </javaOptions> + <ignore>${noServer}</ignore> + </configuration> + </execution> + <execution> + <id>start</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <spawn>true</spawn> + <testURI>tcp://localhost:61616</testURI> + <args> + <param>run</param> + </args> + </configuration> + </execution> + <execution> + <id>runClient</id> + <goals> + <goal>runClient</goal> + </goals> + <configuration> + <clientClass>org.apache.activemq.artemis.jms.example.XAHeuristicExample</clientClass> + </configuration> + </execution> + <execution> + <id>stop</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <args> + <param>stop</param> + </args> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>xa-heuristic</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> + +</project> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-heuristic/readme.html ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-heuristic/readme.html b/examples/broker-features/standard/xa-heuristic/readme.html new file mode 100644 index 0000000..bf44937 --- /dev/null +++ b/examples/broker-features/standard/xa-heuristic/readme.html @@ -0,0 +1,48 @@ +<!-- +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. +--> + +<html> + <head> + <title>ActiveMQ Artemis JMS XA Heuristic Example</title> + <link rel="stylesheet" type="text/css" href="../../../common/common.css" /> + <link rel="stylesheet" type="text/css" href="../../../common/prettify.css" /> + <script type="text/javascript" src="../../../common/prettify.js"></script> + </head> + <body onload="prettyPrint()"> + <h1>JMS XA Heuristic Example</h1> + + <pre>To run the example, simply type <b>mvn verify</b> from this directory, <br>or <b>mvn -PnoServer verify</b> if you want to start and create the server manually.</pre> + + <p>This example shows you how to make an XA heuristic decision through the ActiveMQ Artemis Management Interface.</p> + + <p>A heuristic decision is a unilateral decision to commit or rollback an XA transaction branch after it has + been prepared. </p> + + <p>In this example we simulate a transaction manager to control the transactions. First we create an XASession + and enlist it in a transaction through its XAResource. We then send a text message, 'hello' and end/prepare the transaction + on the XAResource, but neither commit nor roll back the transaction. Another transaction is created and + associated with the same XAResource, and a second message, 'world' is sent on behalf of the second transaction. Again we leave + the second transaction in prepare state. + Then we get the MBeanServerConnection object to manipulate the prepared transactions. To illustrate, we roll back the first + transaction but commit the second. This will result in that only the message 'world' is received. </p> + + <p>This example uses JMX to manipulate transactions in a ActiveMQ Artemis Server. For details on JMX facilities with ActiveMQ Artemis, + please look at the JMX Example.</p> + </body> +</html> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java new file mode 100644 index 0000000..8b6be80 --- /dev/null +++ b/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java @@ -0,0 +1,184 @@ +/* + * 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.activemq.artemis.jms.example; + +import javax.transaction.xa.Xid; + +import org.apache.activemq.artemis.utils.Base64; + +public class DummyXid implements Xid { + + private static final long serialVersionUID = 407053232840068514L; + + private final byte[] branchQualifier; + + private final int formatId; + + private final byte[] globalTransactionId; + + private int hash; + + private boolean hashCalculated; + + // Static -------------------------------------------------------- + + public static String toBase64String(final Xid xid) { + return Base64.encodeBytes(DummyXid.toByteArray(xid)); + } + + private static byte[] toByteArray(final Xid xid) { + byte[] branchQualifier = xid.getBranchQualifier(); + byte[] globalTransactionId = xid.getGlobalTransactionId(); + int formatId = xid.getFormatId(); + + byte[] hashBytes = new byte[branchQualifier.length + globalTransactionId.length + 4]; + System.arraycopy(branchQualifier, 0, hashBytes, 0, branchQualifier.length); + System.arraycopy(globalTransactionId, 0, hashBytes, branchQualifier.length, globalTransactionId.length); + byte[] intBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + intBytes[i] = (byte) ((formatId >> i * 8) % 0xFF); + } + System.arraycopy(intBytes, 0, hashBytes, branchQualifier.length + globalTransactionId.length, 4); + return hashBytes; + } + + // Constructors -------------------------------------------------- + + /** + * Standard constructor + * + * @param branchQualifier + * @param formatId + * @param globalTransactionId + */ + public DummyXid(final byte[] branchQualifier, final int formatId, final byte[] globalTransactionId) { + this.branchQualifier = branchQualifier; + this.formatId = formatId; + this.globalTransactionId = globalTransactionId; + } + + /** + * Copy constructor + * + * @param other + */ + public DummyXid(final Xid other) { + branchQualifier = copyBytes(other.getBranchQualifier()); + formatId = other.getFormatId(); + globalTransactionId = copyBytes(other.getGlobalTransactionId()); + } + + // Xid implementation ------------------------------------------------------------------ + + public byte[] getBranchQualifier() { + return branchQualifier; + } + + public int getFormatId() { + return formatId; + } + + public byte[] getGlobalTransactionId() { + return globalTransactionId; + } + + // Public ------------------------------------------------------------------------------- + + @Override + public int hashCode() { + if (!hashCalculated) { + calcHash(); + } + return hash; + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Xid)) { + return false; + } + Xid xother = (Xid) other; + if (xother.getFormatId() != formatId) { + return false; + } + if (xother.getBranchQualifier().length != branchQualifier.length) { + return false; + } + if (xother.getGlobalTransactionId().length != globalTransactionId.length) { + return false; + } + for (int i = 0; i < branchQualifier.length; i++) { + byte[] otherBQ = xother.getBranchQualifier(); + if (branchQualifier[i] != otherBQ[i]) { + return false; + } + } + for (int i = 0; i < globalTransactionId.length; i++) { + byte[] otherGtx = xother.getGlobalTransactionId(); + if (globalTransactionId[i] != otherGtx[i]) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "XidImpl (" + System.identityHashCode(this) + + " bq:" + + stringRep(branchQualifier) + + " formatID:" + + formatId + + " gtxid:" + + stringRep(globalTransactionId); + } + + // Private ------------------------------------------------------------------------------- + + private String stringRep(final byte[] bytes) { + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + + buff.append(b); + + if (i != bytes.length - 1) { + buff.append('.'); + } + } + + return buff.toString(); + } + + private void calcHash() { + byte[] hashBytes = org.apache.activemq.artemis.jms.example.DummyXid.toByteArray(this); + String s = new String(hashBytes); + hash = s.hashCode(); + hashCalculated = true; + } + + private byte[] copyBytes(final byte[] other) { + byte[] bytes = new byte[other.length]; + + System.arraycopy(other, 0, bytes, 0, other.length); + + return bytes; + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java b/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java new file mode 100644 index 0000000..d233731 --- /dev/null +++ b/examples/broker-features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java @@ -0,0 +1,219 @@ +/* + * 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.activemq.artemis.jms.example; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.XAConnection; +import javax.jms.XAConnectionFactory; +import javax.jms.XASession; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import javax.naming.InitialContext; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder; +import org.apache.activemq.artemis.utils.UUIDGenerator; + +/** + * A simple JMS example showing how to administer un-finished transactions. + */ +public class XAHeuristicExample { + + private static final String JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:3001/jmxrmi"; + + public static void main(final String[] args) throws Exception { + Boolean result = true; + final ArrayList<String> receiveHolder = new ArrayList<String>(); + XAConnection connection = null; + InitialContext initialContext = null; + try { + // Step 1. Create an initial context to perform the JNDI lookup. + initialContext = new InitialContext(); + + // Step 2. Lookup on the queue + Queue queue = (Queue) initialContext.lookup("queue/exampleQueue"); + + // Step 3. Perform a lookup on the XA Connection Factory + XAConnectionFactory cf = (XAConnectionFactory) initialContext.lookup("XAConnectionFactory"); + + // Step 4.Create a JMS XAConnection + connection = cf.createXAConnection(); + + // Step 5. Start the connection + connection.start(); + + // Step 6. Create a JMS XASession + XASession xaSession = connection.createXASession(); + + // Step 7. Create a normal session + Session normalSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 8. Create a normal Message Consumer + MessageConsumer normalConsumer = normalSession.createConsumer(queue); + normalConsumer.setMessageListener(new SimpleMessageListener(receiveHolder, result)); + + // Step 9. Get the JMS Session + Session session = xaSession.getSession(); + + // Step 10. Create a message producer + MessageProducer producer = session.createProducer(queue); + + // Step 11. Create two Text Messages + TextMessage helloMessage = session.createTextMessage("hello"); + TextMessage worldMessage = session.createTextMessage("world"); + + // Step 12. create a transaction + Xid xid1 = new DummyXid("xa-example1".getBytes(StandardCharsets.ISO_8859_1), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 13. Get the JMS XAResource + XAResource xaRes = xaSession.getXAResource(); + + // Step 14. Begin the Transaction work + xaRes.start(xid1, XAResource.TMNOFLAGS); + + // Step 15. do work, sending hello message. + producer.send(helloMessage); + + System.out.println("Sent message " + helloMessage.getText()); + + // Step 16. Stop the work for xid1 + xaRes.end(xid1, XAResource.TMSUCCESS); + + // Step 17. Prepare xid1 + xaRes.prepare(xid1); + + // Step 18. Check none should be received + checkNoMessageReceived(receiveHolder); + + // Step 19. Create another transaction. + Xid xid2 = new DummyXid("xa-example2".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 20. Begin the transaction work + xaRes.start(xid2, XAResource.TMNOFLAGS); + + // Step 21. Send the second message + producer.send(worldMessage); + + System.out.println("Sent message " + worldMessage.getText()); + + // Step 22. Stop the work for xid2 + xaRes.end(xid2, XAResource.TMSUCCESS); + + // Step 23. prepare xid2 + xaRes.prepare(xid2); + + // Step 24. Again, no messages should be received! + checkNoMessageReceived(receiveHolder); + + // Step 25. Create JMX Connector to connect to the server's MBeanServer + JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(JMX_URL), new HashMap<String, String>()); + + // Step 26. Retrieve the MBeanServerConnection + MBeanServerConnection mbsc = connector.getMBeanServerConnection(); + + // Step 27. List the prepared transactions + ObjectName serverObject = ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName(); + String[] infos = (String[]) mbsc.invoke(serverObject, "listPreparedTransactions", null, null); + + System.out.println("Prepared transactions: "); + for (String i : infos) { + System.out.println(i); + } + + // Step 28. Roll back the first transaction + mbsc.invoke(serverObject, "rollbackPreparedTransaction", new String[]{DummyXid.toBase64String(xid1)}, new String[]{"java.lang.String"}); + + // Step 29. Commit the second one + mbsc.invoke(serverObject, "commitPreparedTransaction", new String[]{DummyXid.toBase64String(xid2)}, new String[]{"java.lang.String"}); + + Thread.sleep(2000); + + // Step 30. Check the result, only the 'world' message received + checkMessageReceived("world", receiveHolder); + + // Step 31. Check the prepared transaction again, should have none. + infos = (String[]) mbsc.invoke(serverObject, "listPreparedTransactions", null, null); + System.out.println("No. of prepared transactions now: " + infos.length); + + // Step 32. Close the JMX Connector + connector.close(); + } + finally { + // Step 32. Be sure to close our JMS resources! + if (initialContext != null) { + initialContext.close(); + } + if (connection != null) { + connection.close(); + } + } + } + + private static void checkMessageReceived(final String value, ArrayList<String> receiveHolder) { + if (receiveHolder.size() != 1) { + throw new IllegalStateException("Number of messages received not correct ! -- " + receiveHolder.size()); + } + String msg = receiveHolder.get(0); + if (!msg.equals(value)) { + throw new IllegalStateException("Received message [" + msg + "], but we expect [" + value + "]"); + } + receiveHolder.clear(); + } + + private static void checkNoMessageReceived(ArrayList<String> receiveHolder) { + if (receiveHolder.size() > 0) { + throw new IllegalStateException("Message received, wrong!"); + } + receiveHolder.clear(); + } +} + +class SimpleMessageListener implements MessageListener { + + ArrayList<String> receiveHolder; + Boolean result; + + SimpleMessageListener(ArrayList<String> receiveHolder, Boolean result) { + this.receiveHolder = receiveHolder; + this.result = result; + } + + public void onMessage(final Message message) { + try { + System.out.println("Message received: " + ((TextMessage) message).getText()); + receiveHolder.add(((TextMessage) message).getText()); + } + catch (JMSException e) { + result = false; + e.printStackTrace(); + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-heuristic/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-heuristic/src/main/resources/jndi.properties b/examples/broker-features/standard/xa-heuristic/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/broker-features/standard/xa-heuristic/src/main/resources/jndi.properties @@ -0,0 +1,21 @@ +# 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. + +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://localhost:61616 +connectionFactory.XAConnectionFactory=tcp://localhost:61616?type=XA_CF +queue.queue/exampleQueue=exampleQueue http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-receive/pom.xml ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-receive/pom.xml b/examples/broker-features/standard/xa-receive/pom.xml new file mode 100644 index 0000000..f421aaf --- /dev/null +++ b/examples/broker-features/standard/xa-receive/pom.xml @@ -0,0 +1,109 @@ +<?xml version='1.0'?> +<!-- +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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>jms-examples</artifactId> + <version>1.0.1-SNAPSHOT</version> + </parent> + + <artifactId>xa-receive</artifactId> + <packaging>jar</packaging> + <name>ActiveMQ Artemis JMS XA Receive Example</name> + + <properties> + <activemq.basedir>${project.basedir}/../../../..</activemq.basedir> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-jms-client</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-maven-plugin</artifactId> + <executions> + <execution> + <id>create</id> + <goals> + <goal>create</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + </configuration> + </execution> + <execution> + <id>start</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <spawn>true</spawn> + <testURI>tcp://localhost:61616</testURI> + <args> + <param>run</param> + </args> + </configuration> + </execution> + <execution> + <id>runClient</id> + <goals> + <goal>runClient</goal> + </goals> + <configuration> + <clientClass>org.apache.activemq.artemis.jms.example.XAReceiveExample</clientClass> + </configuration> + </execution> + <execution> + <id>stop</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <args> + <param>stop</param> + </args> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>xa-receive</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> + +</project> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-receive/readme.html ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-receive/readme.html b/examples/broker-features/standard/xa-receive/readme.html new file mode 100644 index 0000000..ab6d7d7 --- /dev/null +++ b/examples/broker-features/standard/xa-receive/readme.html @@ -0,0 +1,48 @@ +<!-- +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. +--> + +<html> + <head> + <title>ActiveMQ Artemis JMS XA Receive Example</title> + <link rel="stylesheet" type="text/css" href="../../../common/common.css" /> + <link rel="stylesheet" type="text/css" href="../../../common/prettify.css" /> + <script type="text/javascript" src="../../../common/prettify.js"></script> + </head> + <body onload="prettyPrint()"> + <h1>JMS XA Receive Example</h1> + + <pre>To run the example, simply type <b>mvn verify</b> from this directory, <br>or <b>mvn -PnoServer verify</b> if you want to start and create the server manually.</pre> + + + <p>This example demonstrates receiving a message within the scope of an XA transaction. When using an XA transaction + the message will only be acknowledged and removed from the queue when the transaction is committed. + If the transaction is not committed the message maybe redelivered after rollback or during XA recovery.</p> + + <p>ActiveMQ Artemis is JTA aware, meaning you can use ActiveMQ Artemis in an XA transactional environment + and participate in XA transactions. It provides the javax.transaction.xa.XAResource interface for that + purpose. Users can get a XAConnectionFactory to create XAConnections and XASessions.</p> + + <p>In this example we simulate a transaction manager to control the transactions. First we create an XASession + for receiving and a normal session for sending. Then we start a new xa transaction and enlist the receiving + XASession through its XAResource. We then send two words, 'hello' and 'world', receive them, and let the + transaction roll back. The received messages are cancelled back to the queue. Next we start + a new transaction with the same XAResource enlisted, but this time we commit the transaction after receiving the + messages. Then we check that no more messages are to be received.</p> + </body> +</html> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java new file mode 100644 index 0000000..4dbe2f8 --- /dev/null +++ b/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java @@ -0,0 +1,184 @@ +/* + * 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.activemq.artemis.jms.example; + +import org.apache.activemq.artemis.utils.Base64; + +import javax.transaction.xa.Xid; + +public class DummyXid implements Xid { + + private static final long serialVersionUID = 407053232840068514L; + + private final byte[] branchQualifier; + + private final int formatId; + + private final byte[] globalTransactionId; + + private int hash; + + private boolean hashCalculated; + + // Static -------------------------------------------------------- + + public static String toBase64String(final Xid xid) { + return Base64.encodeBytes(DummyXid.toByteArray(xid)); + } + + private static byte[] toByteArray(final Xid xid) { + byte[] branchQualifier = xid.getBranchQualifier(); + byte[] globalTransactionId = xid.getGlobalTransactionId(); + int formatId = xid.getFormatId(); + + byte[] hashBytes = new byte[branchQualifier.length + globalTransactionId.length + 4]; + System.arraycopy(branchQualifier, 0, hashBytes, 0, branchQualifier.length); + System.arraycopy(globalTransactionId, 0, hashBytes, branchQualifier.length, globalTransactionId.length); + byte[] intBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + intBytes[i] = (byte) ((formatId >> i * 8) % 0xFF); + } + System.arraycopy(intBytes, 0, hashBytes, branchQualifier.length + globalTransactionId.length, 4); + return hashBytes; + } + + // Constructors -------------------------------------------------- + + /** + * Standard constructor + * + * @param branchQualifier + * @param formatId + * @param globalTransactionId + */ + public DummyXid(final byte[] branchQualifier, final int formatId, final byte[] globalTransactionId) { + this.branchQualifier = branchQualifier; + this.formatId = formatId; + this.globalTransactionId = globalTransactionId; + } + + /** + * Copy constructor + * + * @param other + */ + public DummyXid(final Xid other) { + branchQualifier = copyBytes(other.getBranchQualifier()); + formatId = other.getFormatId(); + globalTransactionId = copyBytes(other.getGlobalTransactionId()); + } + + // Xid implementation ------------------------------------------------------------------ + + public byte[] getBranchQualifier() { + return branchQualifier; + } + + public int getFormatId() { + return formatId; + } + + public byte[] getGlobalTransactionId() { + return globalTransactionId; + } + + // Public ------------------------------------------------------------------------------- + + @Override + public int hashCode() { + if (!hashCalculated) { + calcHash(); + } + return hash; + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Xid)) { + return false; + } + Xid xother = (Xid) other; + if (xother.getFormatId() != formatId) { + return false; + } + if (xother.getBranchQualifier().length != branchQualifier.length) { + return false; + } + if (xother.getGlobalTransactionId().length != globalTransactionId.length) { + return false; + } + for (int i = 0; i < branchQualifier.length; i++) { + byte[] otherBQ = xother.getBranchQualifier(); + if (branchQualifier[i] != otherBQ[i]) { + return false; + } + } + for (int i = 0; i < globalTransactionId.length; i++) { + byte[] otherGtx = xother.getGlobalTransactionId(); + if (globalTransactionId[i] != otherGtx[i]) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "XidImpl (" + System.identityHashCode(this) + + " bq:" + + stringRep(branchQualifier) + + " formatID:" + + formatId + + " gtxid:" + + stringRep(globalTransactionId); + } + + // Private ------------------------------------------------------------------------------- + + private String stringRep(final byte[] bytes) { + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + + buff.append(b); + + if (i != bytes.length - 1) { + buff.append('.'); + } + } + + return buff.toString(); + } + + private void calcHash() { + byte[] hashBytes = DummyXid.toByteArray(this); + String s = new String(hashBytes); + hash = s.hashCode(); + hashCalculated = true; + } + + private byte[] copyBytes(final byte[] other) { + byte[] bytes = new byte[other.length]; + + System.arraycopy(other, 0, bytes, 0, other.length); + + return bytes; + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java b/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java new file mode 100644 index 0000000..4c4448d --- /dev/null +++ b/examples/broker-features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java @@ -0,0 +1,145 @@ +/* + * 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.activemq.artemis.jms.example; + +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.XAConnection; +import javax.jms.XAConnectionFactory; +import javax.jms.XASession; +import javax.naming.InitialContext; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; +import java.nio.charset.StandardCharsets; + +import org.apache.activemq.artemis.utils.UUIDGenerator; + +/** + * A simple JMS example showing the usage of XA support in JMS. + */ +public class XAReceiveExample { + + public static void main(final String[] args) throws Exception { + XAConnection connection = null; + InitialContext initialContext = null; + try { + // Step 1. Create an initial context to perform the JNDI lookup. + initialContext = new InitialContext(); + + // Step 2. Lookup on the queue + Queue queue = (Queue) initialContext.lookup("queue/exampleQueue"); + + // Step 3. Perform a lookup on the XA Connection Factory + XAConnectionFactory cf = (XAConnectionFactory) initialContext.lookup("XAConnectionFactory"); + + // Step 4.Create a JMS XAConnection + connection = cf.createXAConnection(); + + // Step 5. Start the connection + connection.start(); + + // Step 6. Create a JMS XASession + XASession xaSession = connection.createXASession(); + + // Step 7. Create a normal session + Session normalSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 8. Create a normal Message Producer + MessageProducer normalProducer = normalSession.createProducer(queue); + + // Step 9. Get the JMS Session + Session session = xaSession.getSession(); + + // Step 10. Create a message consumer + MessageConsumer xaConsumer = session.createConsumer(queue); + + // Step 11. Create two Text Messages + TextMessage helloMessage = session.createTextMessage("hello"); + TextMessage worldMessage = session.createTextMessage("world"); + + // Step 12. create a transaction + Xid xid1 = new DummyXid("xa-example1".getBytes(StandardCharsets.US_ASCII), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 13. Get the JMS XAResource + XAResource xaRes = xaSession.getXAResource(); + + // Step 14. Begin the Transaction work + xaRes.start(xid1, XAResource.TMNOFLAGS); + + // Step 15. Send two messages. + normalProducer.send(helloMessage); + normalProducer.send(worldMessage); + + // Step 16. Receive the message + TextMessage rm1 = (TextMessage) xaConsumer.receive(); + System.out.println("Message received: " + rm1.getText()); + TextMessage rm2 = (TextMessage) xaConsumer.receive(); + System.out.println("Message received: " + rm2.getText()); + + // Step 17. Stop the work + xaRes.end(xid1, XAResource.TMSUCCESS); + + // Step 18. Prepare + xaRes.prepare(xid1); + + // Step 19. Roll back the transaction + xaRes.rollback(xid1); + + // Step 20. Create another transaction + Xid xid2 = new DummyXid("xa-example2".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 21. Start the transaction + xaRes.start(xid2, XAResource.TMNOFLAGS); + + // Step 22. receive those messages again + rm1 = (TextMessage) xaConsumer.receive(); + System.out.println("Message received again: " + rm1.getText()); + rm2 = (TextMessage) xaConsumer.receive(); + System.out.println("Message received again: " + rm2.getText()); + + // Step 23. Stop the work + xaRes.end(xid2, XAResource.TMSUCCESS); + + // Step 24. Prepare + xaRes.prepare(xid2); + + // Step 25. Commit! + xaRes.commit(xid2, false); + + // Step 26. Check no more messages are received. + TextMessage rm3 = (TextMessage) xaConsumer.receive(2000); + if (rm3 == null) { + System.out.println("No message received after commit."); + } + else { + throw new IllegalStateException(); + } + } + finally { + // Step 27. Be sure to close our JMS resources! + if (initialContext != null) { + initialContext.close(); + } + if (connection != null) { + connection.close(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-receive/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-receive/src/main/resources/jndi.properties b/examples/broker-features/standard/xa-receive/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/broker-features/standard/xa-receive/src/main/resources/jndi.properties @@ -0,0 +1,21 @@ +# 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. + +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://localhost:61616 +connectionFactory.XAConnectionFactory=tcp://localhost:61616?type=XA_CF +queue.queue/exampleQueue=exampleQueue http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-send/pom.xml ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-send/pom.xml b/examples/broker-features/standard/xa-send/pom.xml new file mode 100644 index 0000000..b4dfdf4 --- /dev/null +++ b/examples/broker-features/standard/xa-send/pom.xml @@ -0,0 +1,109 @@ +<?xml version='1.0'?> +<!-- +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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>jms-examples</artifactId> + <version>1.0.1-SNAPSHOT</version> + </parent> + + <artifactId>xa-send</artifactId> + <packaging>jar</packaging> + <name>ActiveMQ Artemis JMS XA Send Example</name> + + <properties> + <activemq.basedir>${project.basedir}/../../../..</activemq.basedir> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-jms-client</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-maven-plugin</artifactId> + <executions> + <execution> + <id>create</id> + <goals> + <goal>create</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + </configuration> + </execution> + <execution> + <id>start</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <spawn>true</spawn> + <testURI>tcp://localhost:61616</testURI> + <args> + <param>run</param> + </args> + </configuration> + </execution> + <execution> + <id>runClient</id> + <goals> + <goal>runClient</goal> + </goals> + <configuration> + <clientClass>org.apache.activemq.artemis.jms.example.XASendExample</clientClass> + </configuration> + </execution> + <execution> + <id>stop</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <args> + <param>stop</param> + </args> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.activemq.examples.broker</groupId> + <artifactId>xa-send</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> + +</project> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-send/readme.html ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-send/readme.html b/examples/broker-features/standard/xa-send/readme.html new file mode 100644 index 0000000..fb3db4c --- /dev/null +++ b/examples/broker-features/standard/xa-send/readme.html @@ -0,0 +1,215 @@ +<!-- +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. +--> + +<html> + <head> + <title>ActiveMQ Artemis JMS XA Send Example</title> + <link rel="stylesheet" type="text/css" href="../../../common/common.css" /> + <link rel="stylesheet" type="text/css" href="../../../common/prettify.css" /> + <script type="text/javascript" src="../../../common/prettify.js"></script> + </head> + <body onload="prettyPrint()"> + <h1>JMS XA Send Example</h1> + <p>This example shows you how message sending behaves in an XA transaction in ActiveMQ Artemis. When a message is sent within + the scope of an XA transaction, it will only reach the queue once the transaction is committed. + If the transaction is rolled back the sent messages will be discarded by the server.</p> + + <p>ActiveMQ Artemis is JTA aware, meaning you can use ActiveMQ Artemis in a XA transactional environment + and participate in XA transactions. It provides the javax.transaction.xa.XAResource interface for that + purpose. Users can get a XAConnectionFactory to create XAConnections and XASessions.</p> + + <p>In this example we simulate a transaction manager to control the transactions. First we create an XASession + and enlist it in a transaction through its XAResource. We then send two words, 'hello' and 'world', with + the session, let the transaction roll back. The messages are discarded and never be received. Next we start + a new transaction with the same XAResource, but this time we commit the transaction. Both messages are received.</p> + + <h2>Example step-by-step</h2> + <p><i>To run the example, simply type <code>mvn verify -Pexample</code> from this directory</i></p> + + <ol> + <li>First we need to get an initial context so we can look-up the JMS connection factory and destination objects from JNDI. This initial context will get it's properties from the <code>client-jndi.properties</code> file in the directory <code>../common/config</code></li> + <pre class="prettyprint"> + <code>InitialContext initialContext = getContext(0);</code> + </pre> + + <li>We look-up the JMS queue object from JNDI</li> + <pre class="prettyprint"> + <code>Queue queue = (Queue) initialContext.lookup("/queue/exampleQueue");</code> + </pre> + + <li>We perform a lookup on the XA Connection Factory</li> + <pre class="prettyprint"> + <code>XAConnectionFactory cf = (XAConnectionFactory) initialContext.lookup("/XAConnectionFactory");</code> + </pre> + + <li>We create a JMS XAConnection</li> + <pre class="prettyprint"> + <code>connection = cf.createXAConnection();</code> + </pre> + + <li>We Start the connection</li> + <pre class="prettyprint"> + <code>connection.start();</code> + </pre> + + <li>We create a JMS XASession</li> + <pre class="prettyprint"> + <code>XASession xaSession = connection.createXASession();</code> + </pre> + + <li>We create a normal session</li> + <pre class="prettyprint"> + <code>Session normalSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);</code> + </pre> + + <li>We create a normal Message Consumer</li> + <pre class="prettyprint"> + <code> + MessageConsumer normalConsumer = normalSession.createConsumer(queue); + normalConsumer.setMessageListener(new SimpleMessageListener()); + </code> + </pre> + + <li>We get the JMS Session</li> + <pre class="prettyprint"> + <code>Session session = xaSession.getSession();</code> + </pre> + + <li>We create a message producer</li> + <pre class="prettyprint"> + <code>MessageProducer producer = session.createProducer(queue);</code> + </pre> + + <li>We create two Text Messages</li> + <pre class="prettyprint"> + <code> + TextMessage helloMessage = session.createTextMessage("hello"); + TextMessage worldMessage = session.createTextMessage("world"); + </code> + </pre> + + <li>We create a transaction</li> + <pre class="prettyprint"> + <code>Xid xid1 = new XidImpl("xa-example1".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes());</code> + </pre> + + <li>We get the JMS XAResource</li> + <pre class="prettyprint"> + <code>XAResource xaRes = xaSession.getXAResource();</code> + </pre> + + <li>We begin the Transaction work</li> + <pre class="prettyprint"> + <code>xaRes.start(xid1, XAResource.TMNOFLAGS);</code> + </pre> + + <li>We do work, sending two messages.</li> + <pre class="prettyprint"> + <code> + producer.send(helloMessage); + producer.send(worldMessage); + </code> + </pre> + + <li>We check the result, it should receive none!</li> + <pre class="prettyprint"> + <code>checkNoMessageReceived();</code> + </pre> + + <li>We stop the work</li> + <pre class="prettyprint"> + <code>xaRes.end(xid1, XAResource.TMSUCCESS);</code> + </pre> + + <li>We prepare</li> + <pre class="prettyprint"> + <code>xaRes.prepare(xid1);</code> + </pre> + + <li>We roll back the transaction </li> + <pre class="prettyprint"> + <code>xaRes.rollback(xid1);</code> + </pre> + + <li>We check no messages should be received! </li> + <pre class="prettyprint"> + <code>checkNoMessageReceived();</code> + </pre> + + <li>We create another transaction</li> + <pre class="prettyprint"> + <code>Xid xid2 = new XidImpl("xa-example2".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes());</code> + </pre> + + <li>We start the transaction</li> + <pre class="prettyprint"> + <code>xaRes.start(xid2, XAResource.TMNOFLAGS);</code> + </pre> + + <li>We re-send those messages</li> + <pre class="prettyprint"> + <code> + producer.send(helloMessage); + producer.send(worldMessage); + </code> + </pre> + + <li>We stop the work</li> + <pre class="prettyprint"> + <code>xaRes.end(xid2, XAResource.TMSUCCESS);</code> + </pre> + + <li>We prepare</li> + <pre class="prettyprint"> + <code>xaRes.prepare(xid2);</code> + </pre> + + <li>We check that no messages should be received at this moment</li> + <pre class="prettyprint"> + <code>checkNoMessageReceived();</code> + </pre> + + <li>We commit!</li> + <pre class="prettyprint"> + <code>xaRes.commit(xid2, false);</code> + </pre> + + <li>We check that all messages are received.</li> + <pre class="prettyprint"> + <code>checkAllMessageReceived();</code> + </pre> + + <li>And finally, <b>always</b> remember to close your JMS connections and resources after use, in a <code>finally</code> block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects</li> + + <pre class="prettyprint"> + <code>finally + { + if (initialContext != null) + { + initialContext.close(); + } + if (connection != null) + { + connection.close(); + } + }</code> + </pre> + </ol> + </body> +</html> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java new file mode 100644 index 0000000..4dbe2f8 --- /dev/null +++ b/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java @@ -0,0 +1,184 @@ +/* + * 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.activemq.artemis.jms.example; + +import org.apache.activemq.artemis.utils.Base64; + +import javax.transaction.xa.Xid; + +public class DummyXid implements Xid { + + private static final long serialVersionUID = 407053232840068514L; + + private final byte[] branchQualifier; + + private final int formatId; + + private final byte[] globalTransactionId; + + private int hash; + + private boolean hashCalculated; + + // Static -------------------------------------------------------- + + public static String toBase64String(final Xid xid) { + return Base64.encodeBytes(DummyXid.toByteArray(xid)); + } + + private static byte[] toByteArray(final Xid xid) { + byte[] branchQualifier = xid.getBranchQualifier(); + byte[] globalTransactionId = xid.getGlobalTransactionId(); + int formatId = xid.getFormatId(); + + byte[] hashBytes = new byte[branchQualifier.length + globalTransactionId.length + 4]; + System.arraycopy(branchQualifier, 0, hashBytes, 0, branchQualifier.length); + System.arraycopy(globalTransactionId, 0, hashBytes, branchQualifier.length, globalTransactionId.length); + byte[] intBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + intBytes[i] = (byte) ((formatId >> i * 8) % 0xFF); + } + System.arraycopy(intBytes, 0, hashBytes, branchQualifier.length + globalTransactionId.length, 4); + return hashBytes; + } + + // Constructors -------------------------------------------------- + + /** + * Standard constructor + * + * @param branchQualifier + * @param formatId + * @param globalTransactionId + */ + public DummyXid(final byte[] branchQualifier, final int formatId, final byte[] globalTransactionId) { + this.branchQualifier = branchQualifier; + this.formatId = formatId; + this.globalTransactionId = globalTransactionId; + } + + /** + * Copy constructor + * + * @param other + */ + public DummyXid(final Xid other) { + branchQualifier = copyBytes(other.getBranchQualifier()); + formatId = other.getFormatId(); + globalTransactionId = copyBytes(other.getGlobalTransactionId()); + } + + // Xid implementation ------------------------------------------------------------------ + + public byte[] getBranchQualifier() { + return branchQualifier; + } + + public int getFormatId() { + return formatId; + } + + public byte[] getGlobalTransactionId() { + return globalTransactionId; + } + + // Public ------------------------------------------------------------------------------- + + @Override + public int hashCode() { + if (!hashCalculated) { + calcHash(); + } + return hash; + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Xid)) { + return false; + } + Xid xother = (Xid) other; + if (xother.getFormatId() != formatId) { + return false; + } + if (xother.getBranchQualifier().length != branchQualifier.length) { + return false; + } + if (xother.getGlobalTransactionId().length != globalTransactionId.length) { + return false; + } + for (int i = 0; i < branchQualifier.length; i++) { + byte[] otherBQ = xother.getBranchQualifier(); + if (branchQualifier[i] != otherBQ[i]) { + return false; + } + } + for (int i = 0; i < globalTransactionId.length; i++) { + byte[] otherGtx = xother.getGlobalTransactionId(); + if (globalTransactionId[i] != otherGtx[i]) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "XidImpl (" + System.identityHashCode(this) + + " bq:" + + stringRep(branchQualifier) + + " formatID:" + + formatId + + " gtxid:" + + stringRep(globalTransactionId); + } + + // Private ------------------------------------------------------------------------------- + + private String stringRep(final byte[] bytes) { + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + + buff.append(b); + + if (i != bytes.length - 1) { + buff.append('.'); + } + } + + return buff.toString(); + } + + private void calcHash() { + byte[] hashBytes = DummyXid.toByteArray(this); + String s = new String(hashBytes); + hash = s.hashCode(); + hashCalculated = true; + } + + private byte[] copyBytes(final byte[] other) { + byte[] bytes = new byte[other.length]; + + System.arraycopy(other, 0, bytes, 0, other.length); + + return bytes; + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java b/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java new file mode 100644 index 0000000..fd87f61 --- /dev/null +++ b/examples/broker-features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java @@ -0,0 +1,191 @@ +/* + * 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.activemq.artemis.jms.example; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.XAConnection; +import javax.jms.XAConnectionFactory; +import javax.jms.XASession; +import javax.naming.InitialContext; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.activemq.artemis.utils.UUIDGenerator; + +/** + * A simple JMS example showing the usage of XA support in JMS. + */ +public class XASendExample { + + public static void main(final String[] args) throws Exception { + AtomicBoolean result = new AtomicBoolean(true); + final ArrayList<String> receiveHolder = new ArrayList<String>(); + XAConnection connection = null; + InitialContext initialContext = null; + try { + // Step 1. Create an initial context to perform the JNDI lookup. + initialContext = new InitialContext(); + + // Step 2. Lookup on the queue + Queue queue = (Queue) initialContext.lookup("queue/exampleQueue"); + + // Step 3. Perform a lookup on the XA Connection Factory + XAConnectionFactory cf = (XAConnectionFactory) initialContext.lookup("XAConnectionFactory"); + + // Step 4.Create a JMS XAConnection + connection = cf.createXAConnection(); + + // Step 5. Start the connection + connection.start(); + + // Step 6. Create a JMS XASession + XASession xaSession = connection.createXASession(); + + // Step 7. Create a normal session + Session normalSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 8. Create a normal Message Consumer + MessageConsumer normalConsumer = normalSession.createConsumer(queue); + normalConsumer.setMessageListener(new SimpleMessageListener(receiveHolder, result)); + + // Step 9. Get the JMS Session + Session session = xaSession.getSession(); + + // Step 10. Create a message producer + MessageProducer producer = session.createProducer(queue); + + // Step 11. Create two Text Messages + TextMessage helloMessage = session.createTextMessage("hello"); + TextMessage worldMessage = session.createTextMessage("world"); + + // Step 12. create a transaction + Xid xid1 = new DummyXid("xa-example1".getBytes(StandardCharsets.UTF_8), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 13. Get the JMS XAResource + XAResource xaRes = xaSession.getXAResource(); + + // Step 14. Begin the Transaction work + xaRes.start(xid1, XAResource.TMNOFLAGS); + + // Step 15. do work, sending two messages. + producer.send(helloMessage); + producer.send(worldMessage); + + Thread.sleep(2000); + + // Step 16. Check the result, it should receive none! + checkNoMessageReceived(receiveHolder); + + // Step 17. Stop the work + xaRes.end(xid1, XAResource.TMSUCCESS); + + // Step 18. Prepare + xaRes.prepare(xid1); + + // Step 19. Roll back the transaction + xaRes.rollback(xid1); + + // Step 20. No messages should be received! + checkNoMessageReceived(receiveHolder); + + // Step 21. Create another transaction + Xid xid2 = new DummyXid("xa-example2".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); + + // Step 22. Start the transaction + xaRes.start(xid2, XAResource.TMNOFLAGS); + + // Step 23. Re-send those messages + producer.send(helloMessage); + producer.send(worldMessage); + + // Step 24. Stop the work + xaRes.end(xid2, XAResource.TMSUCCESS); + + // Step 25. Prepare + xaRes.prepare(xid2); + + // Step 26. No messages should be received at this moment + checkNoMessageReceived(receiveHolder); + + // Step 27. Commit! + xaRes.commit(xid2, false); + + Thread.sleep(2000); + + // Step 28. Check the result, all message received + checkAllMessageReceived(receiveHolder); + + if (!result.get()) + throw new IllegalStateException(); + } + finally { + // Step 29. Be sure to close our JMS resources! + if (initialContext != null) { + initialContext.close(); + } + if (connection != null) { + connection.close(); + } + } + } + + private static void checkAllMessageReceived(ArrayList<String> receiveHolder) { + if (receiveHolder.size() != 2) { + throw new IllegalStateException("Number of messages received not correct ! -- " + receiveHolder.size()); + } + receiveHolder.clear(); + } + + private static void checkNoMessageReceived(ArrayList<String> receiveHolder) { + if (receiveHolder.size() > 0) { + throw new IllegalStateException("Message received, wrong!"); + } + receiveHolder.clear(); + } +} + +class SimpleMessageListener implements MessageListener { + + ArrayList<String> receiveHolder; + AtomicBoolean result; + + public SimpleMessageListener(ArrayList<String> receiveHolder, AtomicBoolean result) { + this.receiveHolder = receiveHolder; + this.result = result; + } + + public void onMessage(final Message message) { + try { + System.out.println("Message received: " + message); + receiveHolder.add(((TextMessage) message).getText()); + } + catch (JMSException e) { + result.set(false); + e.printStackTrace(); + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/standard/xa-send/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/broker-features/standard/xa-send/src/main/resources/jndi.properties b/examples/broker-features/standard/xa-send/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/broker-features/standard/xa-send/src/main/resources/jndi.properties @@ -0,0 +1,21 @@ +# 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. + +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://localhost:61616 +connectionFactory.XAConnectionFactory=tcp://localhost:61616?type=XA_CF +queue.queue/exampleQueue=exampleQueue http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/21bf4406/examples/broker-features/sub-modules/aerogear/pom.xml ---------------------------------------------------------------------- diff --git a/examples/broker-features/sub-modules/aerogear/pom.xml b/examples/broker-features/sub-modules/aerogear/pom.xml new file mode 100644 index 0000000..c522d78 --- /dev/null +++ b/examples/broker-features/sub-modules/aerogear/pom.xml @@ -0,0 +1,125 @@ +<?xml version='1.0'?> +<!-- +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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.activemq.examples.modules</groupId> + <artifactId>broker-modules</artifactId> + <version>1.0.1-SNAPSHOT</version> + </parent> + + <properties> + <endpoint/> + <applicationid/> + <mastersecret/> + <activemq.basedir>${project.basedir}/../../../..</activemq.basedir> + </properties> + + <artifactId>aerogear</artifactId> + <packaging>jar</packaging> + <name>ActiveMQ Artemis JMS AeroGear Example</name> + + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-cli</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-maven-plugin</artifactId> + <executions> + <execution> + <id>create</id> + <goals> + <goal>create</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <!-- this list was extracted from mvn dependency:tree on integration/aerogear --> + <libList> + <param>org.apache.activemq:artemis-aerogear-integration:${project.version}</param> + <param>org.jboss.aerogear:unifiedpush-java-client:1.0.0</param> + <param>net.iharder:base64:2.3.8</param> + <param>com.fasterxml.jackson.core:jackson-annotations:2.3.0</param> + <param>com.fasterxml.jackson.core:jackson-core:2.3.0</param> + <param>org.jboss.resteasy:resteasy-jackson-provider:2.3.2.Final</param> + <param>org.codehaus.jackson:jackson-core-asl:1.8.5</param> + <param>org.codehaus.jackson:jackson-mapper-asl:1.8.5</param> + <param>org.codehaus.jackson:jackson-jaxrs:1.8.5</param> + <param>org.codehaus.jackson:jackson-xc:1.8.5</param> + </libList> + </configuration> + </execution> + <execution> + <id>start</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <spawn>true</spawn> + <testURI>tcp://localhost:61616</testURI> + <args> + <param>run</param> + </args> + </configuration> + </execution> + <execution> + <id>runClient</id> + <goals> + <goal>runClient</goal> + </goals> + <configuration> + <clientClass>org.apache.activemq.artemis.jms.example.AerogearExample</clientClass> + </configuration> + </execution> + <execution> + <id>stop</id> + <goals> + <goal>cli</goal> + </goals> + <configuration> + <ignore>${noServer}</ignore> + <args> + <param>stop</param> + </args> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.activemq.examples.modules</groupId> + <artifactId>aerogear</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> + +</project>
