diff --git a/README.md b/README.md index 5e74249c..6bc51ba9 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Java EE samples: - Websocket - REST service over HTTPS - Logging with JUL and KumuluzEE +- JMS KumuluzEE extensions - samples: - KumuluzEE Config diff --git a/jms/README.md b/jms/README.md new file mode 100644 index 00000000..dd462789 --- /dev/null +++ b/jms/README.md @@ -0,0 +1,335 @@ +# KumuluzEE JMS ActiveMQ sample + +> Develop a basic JMS client within a REST service and pack it as a KumuluzEE microservice. + +The objective of this sample is to demonstrate how to use KumuluzEE JMS module. The tutorial guides you through the development of a simple JMS client and will show how to send an object to a queue and how to retrieve it. The methods that will send and receive messages will be exposed via a REST service. Required knowledge: basic familiarity with JMS and basic concepts of REST and JSON. + +## Requirements + +In order to run this example you will need the following: + +1. Java 8 (or newer), you can use any implementation: + * If you have installed Java, you can check the version by typing the following in a command line: + + ``` + java -version + ``` + +2. Maven 3.2.1 (or newer): + * If you have installed Maven, you can check the version by typing the following in a command line: + + ``` + mvn -version + ``` +3. Git: + * If you have installed Git, you can check the version by typing the following in a command line: + + ``` + git --version + ``` +4. ActiveMQ: + * ActiveMQ can be downloaded on the following + [link](http://activemq.apache.org/download.html) + + +## Prerequisites + +This sample does not contain any prerequisites and can be started on its own. + +## Usage + +This sample uses ActiveMQ implementation of JMS. + +1. Run ActiveMQ +* Unix/Linux: + ```bash + $ cd path_to_activemq/bin + $ activemq start + ``` +* Windows: + ```batch + cd "path_to_activemq\bin" + activemq start + ``` +Default location for ActiveMQ console should be http://localhost:8161/admin + +2. The example uses maven to build and run the microservice. Build the sample using maven: + + ```bash + $ cd jms + $ mvn clean package + ``` + +3. Run the sample: +* Uber-jar: + + ```bash + $ java -jar target/${project.build.finalName}.jar + ``` + + in Windows environemnt use the command + ```batch + java -jar target/${project.build.finalName}.jar + ``` + +* Exploded: + + ```bash + $ java -cp target/classes:target/dependency/* com.kumuluz.ee.EeApplication + ``` + + in Windows environment use the command + ```batch + java -cp target/classes;target/dependency/* com.kumuluz.ee.EeApplication + ``` + + +The application/service can be accessed on the following URL: +* JAX-RS REST resource - http://localhost:8080/v1/customers + + +To shut down the example simply stop the processes in the foreground. + +## Tutorial + +This tutorial will guide you through the steps required to create a simple JMS client using ActiveMQ and pack it as a KumuluzEE microservice. +We will develop a simple Customer REST service with the following resources: +* GET http://localhost:8080/v1/customers – get customer from ActiveMQ +* POST http://localhost:8080/v1/customers – add a customer to ActiveMQ + +We will follow these steps: +* Create a Maven project in the IDE of your choice (Eclipse, IntelliJ, etc.) +* Add Maven dependencies to KumuluzEE and include KumuluzEE components (Core, JMS and JAX-RS) +* Implement the JMS client +* Implement the service using standard JAX-RS 2 API +* Build the microservice +* Run it + +### Add Maven dependencies + +Add the KumuluzEE BOM module dependency to your `pom.xml` file: +```xml + + + + com.kumuluz.ee + kumuluzee-bom + ${kumuluz.version} + pom + import + + + +``` + +Add the `kumuluzee-core`, `kumuluzee-servlet-jetty`, `kumuluzee-jax-rs-jersey` and `kumuluzee-jms-activemq` dependencies: +```xml + + + com.kumuluz.ee + kumuluzee-core + + + com.kumuluz.ee + kumuluzee-servlet-jetty + + + com.kumuluz.ee + kumuluzee-jax-rs-jersey + + + com.kumuluz.ee + kumuluzee-jms-activemq + + +``` + +Add the `kumuluzee-maven-plugin` build plugin to package microservice as uber-jar: + +```xml + + + + com.kumuluz.ee + kumuluzee-maven-plugin + ${kumuluzee.version} + + + package + + repackage + + + + + + +``` + +or exploded: + +Add the `kumuluzee-maven-plugin` build plugin to package microservice as exploded: + +```xml + + + + com.kumuluz.ee + kumuluzee-maven-plugin + ${kumuluzee.version} + + + package + + copy-dependencies + + + + + + +``` + +### Implement the REST service + +Register your module as JAX-RS service and define the application path. You could do that in web.xml or for example with `@ApplicationPath` annotation: + +```java +@ApplicationPath("v1") +public class CustomerApplication extends Application { +} +``` + +Implement JAX-RS resource, for example, to implement resource `customers`. The POST method is going to send the input customer directly to JMS queue while the GET method is going to retrieve the next pending `Customer` object from queue: + +```java +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("customers") +public class CustomerResource { + + @POST + public Response addCustomerToQueue(Customer customer) { + QueueHandler.addToQueue(customer); + return Response.noContent().build(); + } + + @GET + public Response readCustomerFromQueue() { + Customer customer = QueueHandler.readFromQueue(); + return customer != null + ? Response.ok(customer).build() + : Response.noContent().build(); + } +} +``` + +Implement the `Customer` Java class, which is a POJO. The class must implement the Serializable interface so we can send this object directly in queue: +```java +public class Customer implements Serializable { + + private String id; + + private String firstName; + + private String lastName; + + // TODO: implement get and set methods +} +``` + +The JMS client logic will be implemented in `QueueHandler`. A sample implementation of this client, can be implemented as follows: + +```java +public class QueueHandler { + + private static Logger LOG = Logger.getLogger(QueueHandler.class.getName()); + + private static String url = ActiveMQConnection.DEFAULT_BROKER_URL; + + private static String queueName = "KUMULUZ_QUEUE"; + + private static int timeout = 1000; + + public static void addToQueue(Customer customer) { + + // Create connection factory and allow all packages for test purpose + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + // Not recommended to trust all packages, only use for testing purposes + connectionFactory.setTrustAllPackages(true); + Connection connection; + + try { + // Create connection + connection = connectionFactory.createConnection(); + connection.start(); + + // create session and producer + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(queueName); + MessageProducer producer = session.createProducer(destination); + + // Create an serializable object to send to queue + ObjectMessage msg = session.createObjectMessage(); + msg.setObject(customer); + msg.setJMSType(Customer.class.getName()); + + // Sending to queue + producer.send(msg); + + connection.close(); + } catch (JMSException e) { + LOG.log(Level.SEVERE ,"JMS threw an error.", e); + } + + } + + public static Customer readFromQueue() { + + // Create connection factory and allow all packages for test purpose + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + // Not recommended to trust all packages, only use for testing purposes + connectionFactory.setTrustAllPackages(true); + Connection connection; + + Customer customer = null; + + try { + // Create connection + connection = connectionFactory.createConnection(); + connection.start(); + + // create session and consumer + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(queueName); + MessageConsumer consumer = session.createConsumer(destination); + + // retrieve message + Message message = consumer.receive(timeout); + + // check if correct type and cast message to Customer + if (message instanceof ObjectMessage && Customer.class.getName().equals(message.getJMSType())) { + ObjectMessage msg = (ObjectMessage) message; + customer = (Customer) msg.getObject(); + } else if (message == null) { + LOG.log(Level.INFO ,"Queue " + queueName +" is empty."); + } else { + LOG.log(Level.INFO ,"Message was not the right type."); + } + + connection.close(); + } catch (JMSException e) { + LOG.log(Level.SEVERE ,"JMS threw an error.", e); + } + + return customer; + } +} +``` + +Method `addToQueue` takes the input customer object and sends it to queue while the `readFromQueue` read the next pending object from queue. + + +### Build the microservice and run it + +To build the microservice and run the example, use the commands as described in previous sections. diff --git a/jms/pom.xml b/jms/pom.xml new file mode 100644 index 00000000..74e1a865 --- /dev/null +++ b/jms/pom.xml @@ -0,0 +1,68 @@ + + + + kumuluzee-samples + com.kumuluz.ee.samples + 2.5.0-SNAPSHOT + + 4.0.0 + + jms + + KumuluzEE JMS sample + JMS usage sample with KumuluzEE + + + + + com.kumuluz.ee + kumuluzee-bom + ${kumuluzee.version} + pom + import + + + + + + + com.kumuluz.ee + kumuluzee-core + + + com.kumuluz.ee + kumuluzee-servlet-jetty + + + + com.kumuluz.ee + kumuluzee-jax-rs-jersey + + + + com.kumuluz.ee + kumuluzee-jms-activemq + + + + + + + com.kumuluz.ee + kumuluzee-maven-plugin + ${kumuluzee.version} + + + package + + repackage + + + + + + + + \ No newline at end of file diff --git a/jms/src/main/java/com/kumuluz/ee/samples/jms/Customer.java b/jms/src/main/java/com/kumuluz/ee/samples/jms/Customer.java new file mode 100644 index 00000000..716c4df5 --- /dev/null +++ b/jms/src/main/java/com/kumuluz/ee/samples/jms/Customer.java @@ -0,0 +1,39 @@ +package com.kumuluz.ee.samples.jms; + +import java.io.Serializable; + +/** + * @author Dejan Ognjenović + * @since 2.4.0 + */ +public class Customer implements Serializable { + + private String id; + + private String firstName; + + private String lastName; + + public String getId() { return id; } + + public void setId(String id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + +} diff --git a/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerApplication.java b/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerApplication.java new file mode 100644 index 00000000..d8b221af --- /dev/null +++ b/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerApplication.java @@ -0,0 +1,12 @@ +package com.kumuluz.ee.samples.jms; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +/** + * @author Dejan Ognjenović + * @since 2.4.0 + */ +@ApplicationPath("v1") +public class CustomerApplication extends Application { +} \ No newline at end of file diff --git a/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerResource.java b/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerResource.java new file mode 100644 index 00000000..44242a8c --- /dev/null +++ b/jms/src/main/java/com/kumuluz/ee/samples/jms/CustomerResource.java @@ -0,0 +1,32 @@ +package com.kumuluz.ee.samples.jms; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * @author Dejan Ognjenović + * @since 2.4.0 + */ +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("customers") +public class CustomerResource { + + @POST + public Response addCustomerToQueue(Customer customer) { + QueueHandler.addToQueue(customer); + return Response.noContent().build(); + } + + @GET + public Response readCustomerFromQueue() { + Customer customer = QueueHandler.readFromQueue(); + return customer != null + ? Response.ok(customer).build() + : Response.noContent().build(); + } + +} + diff --git a/jms/src/main/java/com/kumuluz/ee/samples/jms/QueueHandler.java b/jms/src/main/java/com/kumuluz/ee/samples/jms/QueueHandler.java new file mode 100644 index 00000000..4094ab39 --- /dev/null +++ b/jms/src/main/java/com/kumuluz/ee/samples/jms/QueueHandler.java @@ -0,0 +1,99 @@ +package com.kumuluz.ee.samples.jms; + +import org.apache.activemq.ActiveMQConnection; +import org.apache.activemq.ActiveMQConnectionFactory; + +import javax.jms.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Dejan Ognjenović + * @since 2.4.0 + */ +public class QueueHandler { + + private static Logger LOG = Logger.getLogger(QueueHandler.class.getName()); + + private static String url = ActiveMQConnection.DEFAULT_BROKER_URL; + + private static String queueName = "KUMULUZ_QUEUE"; + + private static int timeout = 1000; + + public static void addToQueue(Customer customer) { + + // Create connection factory and allow all packages for test purpose + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + // Not recommended to trust all packages, only use for testing purposes + connectionFactory.setTrustAllPackages(true); + Connection connection; + + try { + // Create connection + connection = connectionFactory.createConnection(); + connection.start(); + + // create session and producer + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(queueName); + MessageProducer producer = session.createProducer(destination); + + // Create an serializable object to send to queue + ObjectMessage msg = session.createObjectMessage(); + msg.setObject(customer); + msg.setJMSType(Customer.class.getName()); + + // Sending to queue + producer.send(msg); + + connection.close(); + } catch (JMSException e) { + LOG.log(Level.SEVERE ,"JMS threw an error.", e); + } + + } + + public static Customer readFromQueue() { + + // Create connection factory and allow all packages for test purpose + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + // Not recommended to trust all packages, only use for testing purposes + connectionFactory.setTrustAllPackages(true); + Connection connection; + + Customer customer = null; + + try { + // Create connection + connection = connectionFactory.createConnection(); + connection.start(); + + // create session and consumer + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(queueName); + MessageConsumer consumer = session.createConsumer(destination); + + // retrieve message + Message message = consumer.receive(timeout); + + // check if correct type and cast message to Customer + if (message instanceof ObjectMessage && Customer.class.getName().equals(message.getJMSType())) { + ObjectMessage msg = (ObjectMessage) message; + customer = (Customer) msg.getObject(); + } else if (message == null) { + LOG.log(Level.INFO ,"Queue " + queueName +" is empty."); + } else { + LOG.log(Level.INFO ,"Message was not the right type."); + } + + connection.close(); + } catch (JMSException e) { + LOG.log(Level.SEVERE ,"JMS threw an error.", e); + } + + return customer; + } +} diff --git a/jms/src/main/resources/webapp/WEB-INF/web.xml b/jms/src/main/resources/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..0e4d4db2 --- /dev/null +++ b/jms/src/main/resources/webapp/WEB-INF/web.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5002c13e..7179bfd8 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ jsf jsp jul + jms kumuluzee-config kumuluzee-config-mp kumuluzee-config-consul @@ -41,6 +42,7 @@ kumuluzee-microProfile-1.2 kumuluzee-blog-samples kumuluzee-reactive-vertx + jms pom