http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6b17d966/examples/features/standard/xa-heuristic/readme.html ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-heuristic/readme.html b/examples/features/standard/xa-heuristic/readme.html new file mode 100644 index 0000000..bf44937 --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/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/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/6b17d966/examples/features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-heuristic/src/main/java/org/apache/activemq/artemis/jms/example/XAHeuristicExample.java b/examples/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/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/6b17d966/examples/features/standard/xa-heuristic/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-heuristic/src/main/resources/jndi.properties b/examples/features/standard/xa-heuristic/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-receive/pom.xml ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-receive/pom.xml b/examples/features/standard/xa-receive/pom.xml new file mode 100644 index 0000000..f421aaf --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-receive/readme.html ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-receive/readme.html b/examples/features/standard/xa-receive/readme.html new file mode 100644 index 0000000..ab6d7d7 --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/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/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/6b17d966/examples/features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-receive/src/main/java/org/apache/activemq/artemis/jms/example/XAReceiveExample.java b/examples/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/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/6b17d966/examples/features/standard/xa-receive/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-receive/src/main/resources/jndi.properties b/examples/features/standard/xa-receive/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-send/pom.xml ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-send/pom.xml b/examples/features/standard/xa-send/pom.xml new file mode 100644 index 0000000..b4dfdf4 --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-send/readme.html ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-send/readme.html b/examples/features/standard/xa-send/readme.html new file mode 100644 index 0000000..fb3db4c --- /dev/null +++ b/examples/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/6b17d966/examples/features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/DummyXid.java b/examples/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/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/6b17d966/examples/features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-send/src/main/java/org/apache/activemq/artemis/jms/example/XASendExample.java b/examples/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/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/6b17d966/examples/features/standard/xa-send/src/main/resources/jndi.properties ---------------------------------------------------------------------- diff --git a/examples/features/standard/xa-send/src/main/resources/jndi.properties b/examples/features/standard/xa-send/src/main/resources/jndi.properties new file mode 100644 index 0000000..77561f7 --- /dev/null +++ b/examples/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/6b17d966/examples/features/sub-modules/aerogear/pom.xml ---------------------------------------------------------------------- diff --git a/examples/features/sub-modules/aerogear/pom.xml b/examples/features/sub-modules/aerogear/pom.xml new file mode 100644 index 0000000..c522d78 --- /dev/null +++ b/examples/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> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6b17d966/examples/features/sub-modules/aerogear/readme.html ---------------------------------------------------------------------- diff --git a/examples/features/sub-modules/aerogear/readme.html b/examples/features/sub-modules/aerogear/readme.html new file mode 100644 index 0000000..3534a4c --- /dev/null +++ b/examples/features/sub-modules/aerogear/readme.html @@ -0,0 +1,157 @@ +<!-- +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 AeroGear 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 AeroGear 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 how you can send a message to a mobile device by leveraging <a href="http://aerogear.org/push/">AeroGears push</a> technology which + provides support for different push notification technologies like Google Cloud Messaging, Apple's APNs or + Mozilla's SimplePush.</p> + + <p>For this example you will need an AeroGear Application running somewhere, a good way to do this is to deploy the + Push Application on <href a="">openshift</href>, you can follow the AeroGear Push 0.X Quickstart.</p> + + <p>Once you have created your AeroGear Push Application you can create a mobile application. Simply log into the application + on the web and create a new mobile application by clicking the 'create' button. Once created you will see an application id + and a master secret, you will need the later to run the example.</p> + + <p>lastly you will need to create a variant. For this example we will be using Android so you will need to create a google project, + this <a href="http://aerogear.org/docs/guides/aerogear-push-android/google-setup/">article</a> explains how to do this. + Once created click on your app then click 'add' to add a variant. choose 'google cloud messaging', enter your google + API key and the project number from your google project and click create</p> + + <p>Now before we run the example we need a mobile application to receive it. Writing a mobile app is beyond the scope + of this example but for testing purposes we have supplied an Android app you can use, simply install on your android phone. + It can be found <a href="http://downloads.jboss.org.apache.activemq/ActiveMQAeroGear.apk">here</a>. For a more in depth mobile + app example visit the AeroGear site.</p> + + <p>Once you have installed the mobile app you will need to configure the following:</p> + <p>AeroGear Unified Push URL : This is the URL where your aerogear server is running, something like http://myapp-mydomain.rhcloud.com + AeroGear Variant ID : This is the ID of the variant you created in AeroGear + AeroGear Variant Secret : This is the secret for your variant + GCM Sender ID : this is the Google project Number you created on Google + Variant : you can use this to target messages if needed. + </p> + + <p>Once you set all these correctly you should get a message saying your mobile app is registered, if you log into + your AeroGear app you should see it registered with the variant.</p> + + + <p>Now to run the example simply run the following command + 'mvn -Dendpoint=my aerogear url -Dapplicationid=my application id -Dmastersecret=my master secret -Djsse.enableSNIExtension=false clean verify'. + If you arent using java 7 you can omit the 'jsse.enableSNIExtension=false'</p> + + <p>You should see something like this in your ActiveMQServer</p> + <ol> + <pre class="prettyprint"> + <code> + Dec 04, 2013 3:25:39 PM org.jboss.aerogear.unifiedpush.SenderClient submitPayload + INFO: HTTP Response code from UnifiedPush Server: 302 + Dec 04, 2013 3:25:39 PM org.jboss.aerogear.unifiedpush.SenderClient submitPayload + INFO: Performing redirect to 'https://myapp-mydomain.rhcloud.com/rest/sender/' + Dec 04, 2013 3:25:40 PM org.jboss.aerogear.unifiedpush.SenderClient submitPayload + INFO: HTTP Response code from UnifiedPush Server: 200 + </code> + </pre> + </ol> + <p>And on your mobile app you should see a message from ActiveMQ</p> + + <p>Now lets look a bit more closely at the configuration in broker.xml</p> + <ol> + <pre class="prettyprint"> + <code> + <queues> + <queue name="jms.queue.exampleQueue"> + <address>jms.queue.exampleQueue</address> + </queue> + </queues> + + <connector-services> + <connector-service name="aerogear-connector"> + <factory-class>org.apache.activemq.integration.aerogear.AeroGearConnectorServiceFactory</factory-class> + <param key="endpoint" value="${endpoint}"/> + <param key="queue" value="jms.queue.exampleQueue"/> + <param key="application-id" value="${applicationid}"/> + <param key="master-secret" value="${mastersecret}"/> + </connector-service> + </connector-services> + </code> + </pre> + </ol> + <p>Firstly you will see that we have to create a core queue so it is available when the connector is started, the following are mandatory parameters:</p> + <ol> + <li>endpoint - The endpoint or URL of you AeroGear application</li> + <li>queue - The name of the queue to consume from</li> + <li>application-id - The application id of your mobile application in AeroGear</li> + <li>master-secret - the secret of your mobile application in AeroGear</li> + </ol> + <p>as well as those there are also the following optional parameters</p> + <ol> + <li>ttl - The time to live for the message once AeroGear receives it</li> + <li>badge - The badge the mobile app should use for the notification</li> + <li>sound - The sound the mobile app should use for the notification</li> + <li>filter - A message filter(selector) to use on the connector</li> + <li>retry-interval - If an error occurs on send, how long before we try again</li> + <li>retry-attempts - How many times we should try to reconnect after an error</li> + <li>variants - A comma separated list of variants that should get the message</li> + <li>aliases - A list of aliases that should get the message</li> + <li>device-types - A list of device types that should get the message</li> + </ol> + <p>More in depth explanations of these can be found in the AeroGear docs.</p> + <p>Now lets look at a snippet of code we used to send the message for our JMS client</p> + <pre class="prettyprint"> + <code> + Queue queue = (Queue)initialContext.lookup("queue/exampleQueue"); + + // Step 3. Perform a lookup on the Connection Factory + ConnectionFactory cf = (ConnectionFactory)initialContext.lookup("/ConnectionFactory"); + + // Step 4.Create a JMS Connection + connection = cf.createConnection(); + + // Step 5. Create a JMS Session + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 6. Create a JMS Message Producer + MessageProducer producer = session.createProducer(queue); + + // Step 7. Create a Text Message + Message message = session.createMessage(); + + message.setStringProperty("AEROGEAR_ALERT", "Hello this is a notification from ActiveMQ"); + + producer.send(message); + </code> + </pre> + <p> The most important thing here is string propert we have set on the message, i.e. 'AEROGEAR_ALERT'. This is the + actual alert that is sent via AeroGear</p> + <p>As well as the alert itself you can override any of the above optional parameters in the same fashionby using the + following propert names: AEROGEAR_SOUND,AEROGEAR_BADGE,AEROGEAR_TTL,AEROGEAR_VARIANTS,AEROGEAR_ALIASES and AEROGEAR_DEVICE_TYPES</p> + </body> +</html> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6b17d966/examples/features/sub-modules/aerogear/src/main/java/org/apache/activemq/artemis/jms/example/AerogearExample.java ---------------------------------------------------------------------- diff --git a/examples/features/sub-modules/aerogear/src/main/java/org/apache/activemq/artemis/jms/example/AerogearExample.java b/examples/features/sub-modules/aerogear/src/main/java/org/apache/activemq/artemis/jms/example/AerogearExample.java new file mode 100644 index 0000000..b412d87 --- /dev/null +++ b/examples/features/sub-modules/aerogear/src/main/java/org/apache/activemq/artemis/jms/example/AerogearExample.java @@ -0,0 +1,77 @@ +/* + * 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.Message; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.naming.InitialContext; + +/** + * A simple JMS Queue example that creates a producer and consumer on a queue and sends then receives a message. + */ +public class AerogearExample { + + 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. Perfom a lookup on the queue + Queue queue = (Queue) initialContext.lookup("queue/exampleQueue"); + + // Step 3. Perform a lookup on the Connection Factory + ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("ConnectionFactory"); + + // Step 4.Create a JMS Connection + connection = cf.createConnection(); + + // Step 5. Create a JMS Session + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Step 6. Create a JMS Message Producer + MessageProducer producer = session.createProducer(queue); + + // Step 7. Create a Text Message + Message message = session.createMessage(); + + message.setStringProperty("AEROGEAR_ALERT", "Hello this is a notification from ActiveMQ"); + + producer.send(message); + + System.out.println("Sent message"); + + System.out.println("now check your mobile app and press enter"); + + System.in.read(); + } + finally { + // Step 12. Be sure to close our JMS resources! + if (initialContext != null) { + initialContext.close(); + } + if (connection != null) { + connection.close(); + } + } + } +}