Sunday, December 21, 2014

The Value of Annotation @Temporal for Date Related Columns

The annotation @Temporal in official javadoc is described like below:
This annotation must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar.

So when the field variable is a java.util.Date or java.util.Calendar, it MUST be annotated by @Temporal.

There are 3 values for @Temporal

  • TemporalType.DATE
  • TemporalType.TIME
  • TemporalType.TIMESTAMP

Here is a demo to show the usage of there 3 values. The Entity 'MyEntity' has a field called 'lastUpdateTime' which is a defined as a java.util.Date variable. let's update it in java like this:

  import java.util.Date;
//...
// MyEntity is the JPA entity, em is EntityManager context
MyEntity e = em.find(MyEntity.class, 1L); //get the row with id=1
e.setLastUpdateTime(new Date());

TemporalType.DATE


If the column annotated with @Temporal(TemporalType.DATE) like this:

  @Temporal(TemporalType.DATE)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 12.49.51_001


TemporalType.TIME


If the column annotatedwith @Temporal(TemporalType.TIME) like this:

  @Temporal(TemporalType.TIME)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 13.06.58_002


TemporalType.TIMESTAMP


If the column annotated with @Temporal(TemporalType.TIMESTAMP) like this:

  @Temporal(TemporalType.TIMESTAMP)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 13.10.14_003

Thursday, December 18, 2014

How to configure primary key's generator in JPA entity for oracle

In JPA there are 4 strategies to automatically generate value for a primary key column:

  • Auto
  • Identity
  • Sequence
  • Table

To some extent, there are only 3 strategies, the last 3 ones. Because the first 'Auto' just let the JPA implementation to choose which one of the rest 3 strategies it will use. For example working with Oracle + EclipseLink 2.5.0,  the "Auto" will result in the strategy "Table" being choosen . This behavior depends on the database and the JPA implementation in your project.

0. Before we start

In this tutorial, Oracle is database and EclipseLink is JPA implementation.  The table name in this demo is T_JUST_FOR_TEST, which only have 1 field 'id' as its primary key for simplicity's sake.

In JPA the annotation for value generation of  primary key is @GeneratedValue

Note : The value generation strategy only  take effect when you try to write back to database from your java application. In other words, if the table is read-only in the Java application,  Any specified strategy is meaningless in Entity class.

1. Use Strategy Identity

Oracle don't support Identity.

2. Use Strategy Sequence

2. 1 Define Sequence in Database

Make sure there is sequence that you can use or you can define a new one like this.

-- Create sequence 
create sequence SEQ_JUST_FOR_TEST
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1;

The created sequence is named 'SEQ_JUST_FOR_TEST'. it starts from 1 and increase 1 for every next value.


2.2 Define Entity class


Use Annotations for primary key column. First define a sequence generator, then use the generator.

package com.shengwang.example;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table(name="T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;

public TJustForTest() {}

public long getId() {
return this.id;
}

public void setId(long id) {
this.id = id;
}
}

In the above Entity definition class, The annotation @SequenceGenerator is used to define a Sequence Generator named "SEQ_GEN", which is then used in the @GeneratedValue annotation.  The attribute sequenceName is the real sequence  name in Oracle. The allocationSize is the increment for every step.  Make sure the value of allocationSize matches the corresponding value in the sequence definition in Oracle.  In the annotation @GeneratedValue, strategy "GenerationType.SEQUENCE" is specified, and generator set to the sequence generator "SEQ_GEN" defined one line above.


Every time when insert a new TJustForTest entity record into the table like below, the primary key id will increase one.

  // em is the injected EntityManager;
TJustForTest t = new TJustForTest();
em.persist(t);

3. Use Strategy Table


3.1 Define table in database


A table need to be defined in database.

create table T_GENERATOR1
(
gen_key VARCHAR2(255) not null,
gen_value NUMBER,
constraint pk_gen_key PRIMARY KEY (gen_key)
)

The table works somehow like a key-value pair or hash table. Every row of the table can  logically be treated as a sequence.  Normally, the data type of key column is string and data type of value column is number. To make it more readable, A string combines schema(MONITOR2012) and table name(T_JUST_FOR_TEST) is used as key in this tutorial.


3.2 Insert one row into the table

insert into T_GENERATOR (Gen_Key,GEN_VALUE) values ('MONITOR2012.T_JUST_FOR_TEST',0)

This one row data has to be insert in to the table T_GENERATOR before we can use it as generator in JPA entity class.


3.3 Define Entity Class


Use Annotations for primary key column. First define a table generator, then use the generator.

package com.shengwang.example;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table(name = "T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@TableGenerator(name="TABLE_GEN",table="T_GENERATOR", pkColumnName = "GEN_KEY", pkColumnValue = "MONITOR2012.T_JUST_FOR_TEST", valueColumnName = "GEN_VALUE", initialValue = 1, allocationSize = 1 )
@GeneratedValue(strategy = GenerationType.TABLE, generator="TABLE_GEN")
private long id;

public TJustForTest() {
}

public long getId() {
return this.id;
}

public void setId(long id) {
this.id = id;
}
}

The annotation @TableGenerator is used to define a table generator named "TABLE_GEN". This table generator is used as the generator for @GeneratedValue.


Let's go through all the rest attributes in annotation @TableGenerator in details.


table is the table name in database used. pkColumnName is the column name of the table.. pkColumnValue is the value of the key. These 2 attributes are logically combined as a where phase in SQL, " Select ... from T_GENERATOR where GEN_KEY='MONITOR2012.T_JUST_FOR_TEST'", to locate the single record in the database table. valueColumnName is the column name to store real id value. initialValue is the first value of the generator. allocationSize is the step to increase. 


This is how these attributes work in logic: Every time JPA implementation need to generate value, it will first find the table table, then find the row using pkColumnName and pkColumnValue, then read the value of valueColumnName, add allocationSize, use it and update the value back to valueColumnName.


Every time when insert a new TJustForTest entity record into the table like below, the primary key id will increase one.

  // em is the injected EntityManager;
TJustForTest t = new TJustForTest();
em.persist(t);

4. Summary

Strategy sequence and strategy table can be used in JPA entity class if working with Oracle. The strategy sequence is oracle dependent, it does not work if you change to other database like MySQL. The Strategy table is database independent, but seems a little bit more complex than sequence strategy.

Tuesday, December 9, 2014

How to create Java classes from existing tables in database for JPA

If JPA is your persistence layer in your project and you have tables already defined in database. You can use JPA tools in Eclipse to automatically create corresponding Java Entity classes from those tables in database.

In this article, Java JPA entities will be created from an oracle database on LAN.

1. what you need

  • Eclipse 4.3 +  ( In this article, Eclipse Java EE version 4.3.2 kepler is used )
  • Database connection configuration in Eclipse. In eclipse, configure a database connection to the oracle database.  If you don't know how to do that, read this article first How to add oracle DB connection in Eclipse.

2. Make sure you have JPA support enabled in your projects

Righ click on the project name and select "Properties"

Snap_2014.12.09 14.30.34_003

Use project facets if project facets not enabled

Snap_2014.12.09 10.02.48_001

Click the "JPA" if it's not selected, then click "OK" to close the project properties window.

Snap_2014.12.09 14.34.20_004

3. create Java classes from tables

After enabling JPA in project properties, now right click you eclipse project name again, you should see a new context menu item "JPA tools" appears. Choose "Generate Entities from tables"

If you see the "JPA tools" menu but the sub menu don't have "Generate Entities from tables"  item, make sure you right click on the project name instead of a package or a class file.

Snap_2014.12.09 14.49.33_006 

Select a database connection to let Eclipse get the tables  used to generated class from.  (See another tutorial on  how to configure a database connection in Eclipse)

Snap_2014.12.09 15.04.08_007

After connected to the database, schema and tables will be listed below, choose the tables you need in your Java project and click "Next"Snap_2014.12.09 15.07.21_008

In this tutorial  2 tables are selected. There is a  foreign key between 2 tables. Logically one  platform can have more than one devices. It's a "1 to many" mapping.

Next Eclipse automatically create associations between tables you select.  Click each association to see the details.

Snap_2014.12.09 15.20.46_009

In the final step, Eclipse offers the chance to change generated Java class name and the field name inside the generated java class.

Snap_2014.12.09 15.28.13_011

Snap_2014.12.09 15.29.02_012

Click "finish" and the Java classes will be created.  For example one of the Entity class created looks like:

Snap_2014.12.09 15.38.05_013

The created Java Entity class is well annotated and ready for use!

Monday, December 8, 2014

How to add oracle DB connection in Eclipse

Have a DB connection in Eclipse will be very helpful if you need use persistence frameworks like Hibernate or JPA.  It's normally a prerequisite for other functions, for example let eclipse create java entities class from database tables automatically.  Here's how to configure oracle connection in Eclipse.

1. what you need

  • Oracle JDBC driver. ( A jar file may look like ojdbcxxx.jar, if you don't have it on your local file system, download it from oracle website)

2. open "Data Source Explorer" view in Eclipse

From menu, select "window"->"Show View"->"Other"

Snap_2014.12.09 11.23.04_014

Next, Choose "Data Source Explorer" from category "Data Management"

Snap_2014.12.09 10.04.24_002

Then you should have the view opened.

3. Add new connection

Right click on the "Database Connection" in the "Data Source Explorer" view we just opened.  choose "New..."

Snap_2014.12.09 10.06.49_003 

Choose database type ( here is Oracle), give a name for this connection (here is "oracle")

Snap_2014.12.09 10.08.52_005

Next, select the oracle driver for this connection by click the follow icon pointed by the arrow.

Snap_2014.12.09 10.13.26_006

Next, choose oracle version (I'm using oracle 11 for this demo, you can pick up your version) ,  and click the "Jar List" tab to set the driver file path

Snap_2014.12.09 10.14.51_007

In the "Jar List" tab, replace the default non-exists oracle driver file, which is ojdbc14.jar, with the real one  in you local file system, which is d:\ojdbc6.jar

Snap_2014.12.09 10.17.27_009

Snap_2014.12.09 10.19.15_010 

Close the above window,  next provide connection information.

Snap_2014.12.09 10.22.01_012

Use "Test Connection" button to make sure every thing is OK, then Finish.

4. After Connection created

You can see the new oracle DB connection appears in the "Data Source Explorer" view.

Snap_2014.12.09 11.50.04_015

You can browse the database in the view within Eclipse.

Tuesday, December 2, 2014

How to disable c3p0 from writing log to screen (stderr)

Even if you have slf4j and logback configured in your project, the c3p0 insists on writing log to system error channel like this:

Snap_2014.12.03 11.17.45_001

The Eclipse console show log from c3p0 in red, because the texts are written to stderr.  The way to dismiss those red part from c3p0 is add two lines at the beginning of your code.

public static void main(String[] args) {
System.setProperty("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "WARNING");
System.setProperty("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");

// rest of your code
}


Here is the final result looks like. the redundant c3p0 log to stderr is gone.



image

Monday, December 1, 2014

How to inject a logger variable by using annotation in Spring

In development, we need logger almost everywhere. you can inject a bean with annotation like inject other beans
package com.shengwangi.demo;
@Component
public class MyClass {
  @Loggable
  Logger logger;
  public void myMethod() {
    // do some thing
    logger.info("use the logger as usual"); 
  }
}
As you can see, annotation @Loggable is used to inject the logger. @Loggable is an annotation that we defined ourselves. Here is how to do it. (It's not my original idea, borrowed from stackoverflow Q&A here by Redder)

1. Define the annotation

Define the loggable annotation.
package com.shengwangi.demo;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Loggable {}
Besides the package and import lines there are 3 annotations before the one-line definition of Loggable. @Retention(RUNTIME) means the Annotation will be retained by the compiler and can be used by VM at run time. @Target(FIELD) means it will be used to annotate fields of class. @Documented it will be documented when you run javadoc to create documents.

2. Define LoggableInjector Class

Let's see the code first.
package com.shengwangi.demo;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

@Service
public class LoggableInjector implements BeanPostProcessor {

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessBeforeInitialization(final Object bean,
      String beanName) throws BeansException {
    ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
      public void doWith(Field field) throws IllegalArgumentException,
          IllegalAccessException {
        // make the field accessible if defined private
        ReflectionUtils.makeAccessible(field);
        if (field.getAnnotation(Loggable.class) != null) {
          Logger log = LoggerFactory.getLogger(bean.getClass());
          field.set(bean, log);
        }
      }
    });
    return bean;
  }
}
The LoggableInjector implements Spring's BeanPostProcessor interface, which give you the chance to do something just before/after creation of beans. What the LoggableInjector class actually do is after creation of a bean object, find the field annotated by @Loggable, which we defined in step 1, then set the field value to a real logger.

3. Use annotation @Loggable to inject logger

Like we already did in the beginning of this article, using the annotation to inject logger to your beans.
@Loggable Logger logger
One thing need to notice is that the real logger is created after the bean object has been initialized, which means you can NOT use logger in the constructor of the bean. Because at that time, the logger field has not be defined, you'll get NullPointerException Error.

Tuesday, November 11, 2014

How to simply print object content to String in Java

During development, you may frequently want to dump a Java object's all contents into a String for debug and  log.  You object has  fields  of primitive data type as well as List and Set.

In java, dump a object to a more meaningful String will need you to override the toString() method. It's a bad practice to assemble the output string by hand, which is tedious and difficult to maintain. The simplest way to create the output String in the toString() method is using ToStringBuilder class from apache.

1. What you need

  • commons-lang3 package from apache.
You can add the dependency in you maven pom.xml. The current stable version of commons-lang3 is 3.3.2, you can change it to an up-to-date version number.
  <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>


2. Override the toString() in the class definition

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class Pet {
int petAge;
String petName;
String petColor;

@Override
public String toString () {
return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);
}
}


The toString() method is very simple, only need one line. The reflectionToString method will find all the fields in this object and return a String for you automatically. You don't need to assemble the fields one by one. You don't need to care about whether any field's value is null.



3. Run a test main

Let's see run a test main to check the result.
package com.shengwang.demo;

public class DemoMain {

public static void main(String[] args) {

Pet dog = new Pet();
dog.petAge=3;
dog.petName = "snowball";
//dog.petColor = "white";

String output = dog.toString();
System.out.println(output); // print Pet[petAge=3,petName=snowball,petColor=<null>]

}
}


The output looks like "Pet[petAge=3,petName=snowball,petColor=<null>]". For most usage that's good enough.



4.Define your own style (optional)

You may notice there is a prefix of the class name directly before the content of the object("Pet" in our case).If you prefer a more succinct output without that prefix. You can simply define your own style class by extending ToStringStyle from commons-lang3 package.
package com.shengwang.demo;

import org.apache.commons.lang3.builder.ToStringStyle;

public class SuccinctToStringStyle extends ToStringStyle {

private static final long serialVersionUID = 1L;

SuccinctToStringStyle() {
super();
this.setUseClassName(false);
this.setUseIdentityHashCode(false);
this.setUseFieldNames(true);
}
}
Then change in the toString() method to use our own style.
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class Pet {
int petAge;
String petName;
String petColor;

public String toString () {
// use your own style to get even more succinct output
return ToStringBuilder.reflectionToString(this,new SuccinctToStringStyle());
}
}
Run the main again and this time you have output like "[petAge=3,petName=snowball,petColor=<null>]". The prefix class name is gone.

The ToStringBuilder  can handle Java array, List, Set and Map also.  Compare to assemble the String youself, It's simple, clean and maintenance-free!

Tuesday, October 14, 2014

Quick Tutorial on ActiveMQ Web Console In 5 minutes

There are some basic operations when developing with ActiveMQ:

All these operations can be done by using a web browser, because ActiveMQ broker provides a web console default on port 8161. For example if you run your ActiveMQ broker:

# ACTIVEMQ_INSTALL_DIR is the path where you install ActiveMQ
ACTIVEMQ_INSTALL_DIR/bin/activemq start

Then you can access the web console  from url http://localhost:8161/admin. Replace the 'localhost' to the server's IP if you try to access the ActiveMQ web console remotely.


image


Browse all Queues (Queues List)


Click the Queues menu or access Http://localhost:8161/admin/queues.jsp , all queues will list out.


image


The highlight "3" means there are 3 messages in the broker waiting to be received.


Browse  all messages in a Queue (Message List)


From Queues List page, You can click a queue's name to browser all messages pending in that queue.


image


Check detailed information of any message


Click on the message Id, then you can see the details of the message. A message is composed by message header and message body.  image



Create a Message manually from web


If the destination queue exists, then from the queue list page, click the Send To link. If the destination queue does not exists, you can click Send from the menu, and give a name of the destination, the queue will be create automatically when sending message.


image


Type the text content, then click send .


image


Delete a specified message in a Queue


From the message list of the Queue, click Delete to remove message.


image


Delete all messages of a Queue


From the Queue list, click Purge.


image

Spring JMS with ActiveMQ - hello world example - receive message using message listener adapter

This tutorial will demo how to receive message by using Spring JMS and ActiveMQ. This tutorial uses Spring JMS's MessageListenerAdapter. Compare to receiving message using MessageListener interface, there are 2 advantages you can get:

  1. Use any POJO class as real message listener, no need to implements any interface in your Java class.
  2. Easy to send message back in most cases.

There are other ways to  receive message from ActiveMQ broker using Spring JMS, examples are provided here

The example on how to send out message with Spring JMS and ActiveMQ is in this article.

1. What you need

  • JDK 1.7+
  • Maven 3.2.1
  • ActiveMQ 5.10.0
  • Spring 4.1.0.RELEASE  (acquired by Maven)
We’ll use maven to manage all dependencies.  To write code there is no need for ActiveMQ binary, since maven will take care of the ActiveMQ library we need. But to run the code, we need the ActiveMQ binary, In this example  we'll run the ActiveMQ broker on a machine of IP 192.168.203.143 with default port 61616. If you run the broker in a different IP or port, don't forget to change the broker URL in Spring configuration file.

2. Configure the maven  pom.xml

<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>spring-jms-activemq-receive-direct</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>

<!-- Spring Version -->
<spring-framework.version>4.1.0.RELEASE</spring-framework.version>

<!-- ActiveMQ Version -->
<activemq.version>5.10.0</activemq.version>

</properties>

<dependencies>
<!-- Spring Artifacts-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- ActiveMQ Artifacts -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>

</dependencies>

<!-- Use JDK 1.7+ -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

As you can see from the maven pom.xml file, we use 3 key artifacts in this example, spring-context, spring-jms and activemq-all.  The first 2 belong to spring framework and the last one is ActiveMQ  implementation for jms.  The final part of the pom file specified the JDK version for compiling.



3. Define the Java Class


We are going to define 2 classes. The first one is a simple spring bean:
package com.shengwang.demo;

import org.springframework.stereotype.Service;

@Service
/**
* POJO class, have handleMessage(...) method.
* The return of handleMessage(...) will be
* automatically send back to message.getJMSReplyTo().
*/
public class JmsMessageListener {

public String handleMessage(String text) {
System.out.println("Received: " + text);
return "ACK from handleMessage";
}
}

The above JmsMessageListener Class is just a POJO java class, which has neither parent class nor interface implementation. The JmsMessageListener annotated by @Service as spring bean. The default listener method that will be invoked is "handleMessage" for MessageListenerAdapter, but can be changed to different method  in Spring Configuration.

Anything return from handleMessage will be send out as a message back to JMS broker. The destination will be the ReplyTo header of the incoming message. If it's not set, reply message will be send to default destination.

One thing need to mention is something called MessageConvertor. MesssageConvertor will be called before and after the method handleMessage. MessageConvertor is set in Spring configuration for MessageListenerAdapter bean. If not specified, a default one from spring will be used. The default Spring  MessageConvertor converts received message to normal Java data types as input parameter of  handleMessage before invoking.  After handleMessage finishes, it converts return to a kind of  JMS message.

In this demo, a received ActiveMQTextMessage  object is transferred to a String object before calling handleMessage. A return String object is transferred back to a TextMessage object.

How or where is this JmsMessageListener  class registered for receiving message? It's all in the spring configuration file. we'll come to this in the below. Now let’s take a glance at the main class.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {

public static void main(String[] args) {
// create Spring context
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");

// sleep for 1 second
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// close application context
((ClassPathXmlApplicationContext)ctx).close();
}
}
In the main, one thing to notice is there is no trace of the message listener we just defined. Because the message listener will be call asynchronously , you do (can)  not to invoke it explicitly.

Also we use ClassPathXmlApplicationContext to lookup all spring beans in spring configuration file called “app-context.xml”.  We wait for 1 second to let our message listener do it's job, then at the end of main, we close the spring context.



4. spring configuration


Our spring configuration file is named “app-context.xml”. It stays in the man resource path /src/main/resources/ to store the spring configuration file. In the configuration file, we define beans in this order:

Connection Factory  bean (ActiveMQ) –> cached Connection Factory  bean ( Spring jms) –> MessageListenerAdapter bean(Spring jms) ->MessageListenerContainer bean(Spring jms).

The cached connection factory bean is necessary because the Spring will open/close connection on every send or receive action.  So in practical there will always a cached connection factory bean beside the direct connection factory bean.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.shengwang.demo" />


<!-- =============================================== -->
<!-- JMS Common, Define Jms connection factory -->
<!-- =============================================== -->
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- brokerURL -->
<constructor-arg index="0" value="tcp://192.168.203.143:61616" />
</bean>

<!-- Pooled Spring connection factory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>



<!-- ============================================================= -->
<!-- JMS receive. -->
<!-- Define MessageListenerAdapter and MessageListenerContainer -->
<!-- ============================================================= -->
<bean id="messageListenerAdapter"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="jmsMessageListener" />
</bean>

<bean id="messageListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destinationName" value="Send2Recv" />
<property name="messageListener" ref="messageListenerAdapter" />
</bean>

</beans>

As you can see from the Spring configuration file, <context:component-scan ...> is used to let spring find out all beans automatically. In our example we have defined only one bean, jmsMessageListener . Next we define connection factories. The ActiveMQ's connection factory bean works as an input of Spring's pooled connection factory. These 2 connection factory beans will be needed both for sending and receiving messages. 

Next we wrap the POJO class JmsMessageListener by Spring MessageListenerAdapter. Our jmsMessageListener bean is constructor parameter of messageListenerAdapter bean.

Finally we define MessageListenerContainer bean. Spring use MessageListenerContainer to manager messageListener beans. After connection factory beans, we define a bean of spring's DefaultMessageListenerContainer . There are 3 parameters set for this bean. connectionFactory is for connection, destinationName is the Queue's name to receive message from.  messageListener refer to our messageListenerAdapter bean which we wrapped our JmsMessageListener  POJO class with Spring MessageMessageListenerAdapter. So it's here we let spring context know where to receive  messages, and what  to do when a message arrives.

Everything is almost done now. Let's review the directory structure for our maven project.
image



5. Run the code


Before you can run the code, you need to make sure the ActiveMQ broker is running. So our jave code can connect to it and receive message from it.   Make sure the broker IP and port are correct in your spring configuration file "app-context.xml". Run the ActiveMQ broker like with this command.
ACTIVEMQ_INSTALL_DIR/bin/activemq start

Now you can run you main class.  you can run it from IDE such as eclipse, or you can run it direct in command line by using maven.
cd spring-jms-activemq-send
# run mvn from project directory
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"

ActiveMQ broker can be managed by browser on port 8161.  My ActiveMQ broker is running on IP 192.168.203.143, so I can access URL http://192.168.203.143:8161/admin to browse all queues and all messages in the queues. If there is no message in the broker, the main will sleep 1 second, do nothing and exit.  To better show how the message listener works, you can either run the sender example in this example, which will send a message to destination "Send2Recv", or create a message manually from ActiveMQ Web Console by using browser (Don't forget to set "ReplyTo" in the created message's header, cause we need it to send reply back in this example).

For demo, I send 2 messages with different ReplyTo in its message header.

image 
Then we run the message listener main. After running the main of this message listener example, the pending messages in Queue "Send2Recv" are gone. Two messages are replied to different destinations.

image


The content of the message is the return String of listener method handleMessage in class JmsMessageListener.

image

Friday, October 10, 2014

Spring JMS with ActiveMQ - hello world example - receive message using direct message listener

This tutorial will demo how to receive message by using Spring JMS and ActiveMQ. This tutorial uses class directly implements Spring JMS's MessageListener or SessionAwareMessageListener interface. There are other ways to  receive message from ActiveMQ broker using Spring JMS, examples are provided here.

The example on how to send out message with Spring JMS and ActiveMQ is in this article

1. What you need

  • JDK 1.7+
  • Maven 3.2.1
  • ActiveMQ 5.10.0
  • Spring 4.1.0.RELEASE  (acquired by Maven)
We’ll use maven to manage all dependencies.  To write code there is no need for ActiveMQ binary, since maven will take care of the ActiveMQ library we need. But to run the code, we need the ActiveMQ binary, In this example  we'll run the ActiveMQ broker on a machine of IP 192.168.203.143 with default port 61616. If you run the broker in a different IP or port, don't forget to change the broker URL in Spring configuration file.

2. Configure the maven  pom.xml

<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>spring-jms-activemq-receive-direct</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>

<!-- Spring -->
<spring-framework.version>4.1.0.RELEASE</spring-framework.version>

<!-- ActiveMQ -->
<activemq.version>5.10.0</activemq.version>

</properties>

<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- ActiveMQ Artifacts -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>

</dependencies>

<!-- Use JDK 1.7+ -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>


As you can see from the maven pom.xml file, we use 3 key artifacts in this example, spring-context, spring-jms and activemq-all.  The first 2 belong to spring framework and the last one is ActiveMQ  implementation for jms.  The final part of the pom file specified the JDK version for compiling.



3. Define the Java Class


We are going to define 2 classes. The first one is a simple spring bean:
package com.shengwang.demo;

import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.command.ActiveMQTextMessage;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.stereotype.Service;

@Service
/**
* Listener Implement Spring SessionAwareMessageListener Interface
*
*/
public class JmsMessageListener implements SessionAwareMessageListener {

@Override
public void onMessage(TextMessage message, Session session) throws JMSException {
// This is the received message
System.out.println("Receive: "+message.getText());

// Let's prepare a reply message - a "ACK" String
ActiveMQTextMessage textMessage = new ActiveMQTextMessage();
textMessage.setText("ACK");

// Message send back to the replyTo address of the income message.
// Like replying an email somehow.
MessageProducer producer = session.createProducer(message.getJMSReplyTo());
producer.send(textMessage);
}
}



In the above JmsMessageListener Class, it implements Spring's SessionAwareMessageListener interface and the method defined in the interface, the onMessage method. This onMessage method will be called asynchronously on every message arriving. Look through the code, we add @service annotation to make JmsMessageListener class a Spring bean. The onMessage takes 2 parameters.  First one is the message, second one is the Session which you can use to send  out a reply message.


You may be aware of that the JmsMessageListener class implements the interface SessionAwareMessageListener instead of interface MessageListener. The only difference is SessionAwareMessageListener will give one more parameter for method onMessage, Session, which can be used to send message back in you listener.


In the onMessage method, we deal with the income message, print out the content to screen, then create a text message and finally send it back. We use the replyTo info of the income message as the destination to send out.  This somehow really seems like the mechanism of replying a email.  If there is no replyTo info set in the message header of the income message, you need to explicit define a destination to reply.



How or where is this JmsMessageListener  class registered for receiving message? It's all in the spring configuration file. we'll come to this in the below. Now let’s take a glance at the main class.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {

public static void main(String[] args) {
// create Spring context
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");

// sleep for 1 second
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// close application context
((ClassPathXmlApplicationContext)ctx).close();
}
}

In the main, one thing to notice is there is no trace of the message listener we just defined. Because the message listener will be call asynchronously , you do (can)  not to invoke it explicitly.
Also we use ClassPathXmlApplicationContext to lookup all spring beans in spring configuration file called “app-context.xml”.  We wait for 1 second to let our message listener do it's job, then at the end of main, we close the spring context.



4. spring configuration


Our spring configuration file is named “app-context.xml”. It stays in the man resource path /src/main/resources/ to store the spring configuration file. In the configuration file, we define beans in this order:

Connection Factory  bean (by jms provider ActiveMQ) –> cached Connection Factory  bean ( by Spring jms) –> MessageListenerContainer bean (by spring jms).

The cached connection factory bean is necessary because the Spring will open/close connection on every send or receive action.  So in practical there will always a cached connection factory bean beside the direct connection factory bean.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.shengwang.demo" />

<!-- =============================================== -->
<!-- JMS Common,Define JMS connection Factory -->
<!-- =============================================== -->
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- brokerURL -->
<constructor-arg index="0" value="tcp://192.168.203.143:61616" />
</bean>

<!-- Pooled Spring connection factory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>



<!-- ============================================================= -->
<!-- JMS Receive,Define MessageListenerContainer -->
<!-- ============================================================= -->
<bean id = "messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="Send2Recv"/>
<property name="messageListener" ref="jmsMessageListener"/>
</bean>

</beans>



As you can see from the Spring configuration file, we use <context:component-scan ...> to let spring find out all beans automatically. In our example we have defined only one bean, jmsMessageListener . Next we define connection factories. The ActiveMQ's connection factory bean works as an input of Spring's pooled connection factory. These 2 connection factory beans will be needed both for sending and receiving messages. 



Spring use MessageListenerContainer to manager messageListener beans. After connection factory beans, we define a bean of spring's DefaultMessageListenerContainer . There are 3 parameters set for this bean. connectionFactory is for connection, destinationName is the Queue's name to receive message from.  messageListener refer to our jmsMessageListener bean which we defined in the Java class JmsMessageListener above. So it's here we let spring context know where to receive  messages, and what  to do when a message arrives.

Everything is almost done now. Let's review the directory structure for our maven project.
image



5. Run the code


Before you can run the code, you need to make sure the ActiveMQ broker is running. So our jave code can connect to it and receive message from it.   Make sure the broker IP and port are correct in your spring configuration file "app-context.xml". Run the ActiveMQ broker like with this command.
ACTIVEMQ_INSTALL_DIR/bin/activemq start

Now you can run you main class.  you can run it from IDE such as eclipse, or you can run it direct in command line by using maven.
cd spring-jms-activemq-send
# run mvn from project directory
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"


ActiveMQ broker can be managed by browser on port 8161.  My ActiveMQ broker is running on IP 192.168.203.143, so I can access URL http://192.168.203.143:8161/admin to browse all queues and all messages in the queues. If there is no message in the broker, the main will sleep 1 second, do nothing and exit.  To better show how the message listener works, you can either run the sender example in this example, which will send a message to destination "Send2Recv", or create a message manually from ActiveMQ Web Console by using browser (Don't forget to set "ReplyTo" in the created message's header, cause we need it to send reply back in this example).


image 
Then we run the message listener main. After running the main of this message listener example, the messages in Queue "Send2Recv" are gone. There are 2 new message on the Queue "Recv2Send", they are our ack reply message which we send back in the onMessage method of the listener.





image

Tuesday, October 7, 2014

Spring JMS with ActiveMQ – hello world example – send message

Before we start, let make some concepts clear .  What’s the role for each part of the JMS/Spring JMS/ActiveMQ combination.
What’s JMS ? In short, JMS is a set of APIs defined in JSR. In practical you can think it’s a set of Java Interfaces. JMS is part of the JEE standard coming with JDK in package javax.jms.*  There are basically 2 versions of JMS, JMS 1.x and JMS 2.0. These 2 sets of APIs are different.
What’s ActiveMQ? In short ActiveMQ is one of the implementation of JMS APIs (Of course there are other implementations, Open JMS, RabbitMQ for example).  ActiveMQ also provide the broker which can be treated somehow  like a server, that you can send message to or receive message from. For now, 2014-10, ActiveMQ only implements the interfaces defined in  JMS 1.1 version. 
What’s Spring JMS? Spring JMS is part of  the whole spring framework. It wrap the real JMS service provider such as ActiveMQ or OpenJMS, provides consistent APIs to upper logic. Spring  JMS’s APIs are quite similar to JMS2.0  in JEE7. Spring JMS can decouple you business logic from the real JMS service provider, which here in our example is apache ActiveMQ.
In this example we are going to create a example to send out a text message by using Spring JMS and ActiveMQ. 

1. What you need

  • JDK 1.7
  • Maven 3.2.1
  • ActiveMQ 5.10.0
  • Spring 4.1.0.RELEASE  (acquired by Maven)
We’ll use maven to manage all dependencies.  To write code there is no need for ActiveMQ binary, since maven will take care of the ActiveMQ library we need. But to run the code, we need the ActiveMQ binary, In this example  we'll run the ActiveMQ broker on a machine of IP 192.168.203.143 with default port 61616. If you run the broker in a different IP or port, don't forget to change the broker URL in Spring configuration file.

2. Configure the maven  pom.xml

<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>spring-jms-activemq-send</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<!-- Spring version -->
<spring-framework.version>4.1.0.RELEASE</spring-framework.version>
<!-- ActiveMQ version -->
<activemq.version>5.10.0</activemq.version>
</properties>

<dependencies>
<!-- Spring aritifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- ActiveMQ Artifacts -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-spring</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>

<!-- Using JDK 1.7 for compiling -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

As you can see from the maven pom.xml file, we use 3 key artifacts in this example, spring-context, spring-jms and activemq-spring.  The first 2 belong to spring framework and the last one is ActiveMQ  implementation for jms.  The final part of the pom file specified the JDK version for compiling.



3. Define the Java Class


We are going to define 2 classes. The first one is a simple spring bean:
package com.shengwang.demo;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

@Service
public class JmsMessageSender {

@Autowired
private JmsTemplate jmsTemplate;


/**
* send text to default destination
* @param text
*/
public void send(final String text) {

this.jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
Message message = session.createTextMessage(text);
//set ReplyTo header of Message, pretty much like the concept of email.
message.setJMSReplyTo(new ActiveMQQueue("Recv2Send"));
return message;
}
});
}

/**
* Simplify the send by using convertAndSend
* @param text
*/
public void sendText(final String text) {
this.jmsTemplate.convertAndSend(text);
}

/**
* Send text message to a specified destination
* @param text
*/
public void send(final Destination dest,final String text) {

this.jmsTemplate.send(dest,new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
Message message = session.createTextMessage(text);
return message;
}
});
}
}

In the above JmsMessageSender Class, Spring's JmsTemplate is used to do the send message task. You can find out how this bean is defined in spring configuration file below.  There are 1 sendText method and 2 send methods in the class. The 2 send methods both take String parameter text as message payload to be sent. The different is the later send take one more Destination parameter to specify where to send the message. the first one will simply send the message to the default destination.  The sendText method simplify the send by using Spring JmsTemplate’s convertAndSend method which will automatically create the TextMessage object from the input String object and send it.  convertAndSend  can save you from creating the TextMessage object yourself.

Next let’s see how to use the above JmsMessageSender class in the main class.
package com.shengwang.demo;

import javax.jms.Queue;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class DemoMain {

public static void main(String[] args) {
// init spring context
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");

// get bean from context
JmsMessageSender jmsMessageSender = (JmsMessageSender)ctx.getBean("jmsMessageSender");

// send to default destination
jmsMessageSender.send("hello JMS");

// send to a code specified destination
Queue queue = new ActiveMQQueue("AnotherDest");
jmsMessageSender.send(queue, "hello Another Message");

// close spring application context
((ClassPathXmlApplicationContext)ctx).close();
}

}

In the main, we send out 2 text message by using the JmsMessageSender bean we have defined.  One thing to notice is how to send out the second message. We create a ActiveMQQueue as destination. and send the second message to this queue. We use ActiveMQ as our JMS service provider. If we use another JMS service provider like OpenJMS, we may need to change ActiveMQQueue  to OpenJMS provided Queue implementation.

Also we use ClassPathXmlApplicationContext to lookup all spring beans in spring configuration file called “app-context.xml”.  At main end, we close the spring context.



4. spring configuration


Our spring configuration file is named “app-context.xml”. It stays in the man resource path /src/main/resources/ to store the spring configuration file. In the configuration file, we define beans in this order:

Connection Factory  bean (by jms provider ActiveMQ) –> cached Connection Factory  bean ( by Spring jms) –> JmsTemplate bean (by spring).

The cached connection factory bean is necessary because the Spring JmsTemplate will open/close connection on every send or receive action.  So in practical there will always a cached connection factory bean beside the direct connection factory bean.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.shengwang.demo" />


<!-- =============================================== -->
<!-- JMS Common, Define JMS connectionFactory -->
<!-- =============================================== -->
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- brokerURL, You may have different IP or port -->
<constructor-arg index="0" value="tcp://192.168.203.143:61616" />
</bean>

<!-- Pooled Spring connection factory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>

<!-- ======================================================= -->
<!-- JMS Send, define default destination and JmsTemplate -->
<!-- ======================================================= -->
<!-- Default Destination Queue Definition -->
<bean id="defaultDestination" class="org.apache.activemq.command.ActiveMQQueue">
<!-- name of the queue -->
<constructor-arg index="0" value="Send2Recv" />
</bean>

<!-- JmsTemplate Definition -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="defaultDestination" />
</bean>

</beans>


As you can see from the Spring configuration file, we use <context:component-scan ...> to let spring find out all beans automatically. In our example we have defined only one bean, jmsMessageSender . Next we define connection factories. The ActiveMQ's connection factory bean works as an input of Spring's pooled connection factory. These 2 connection factory beans will be needed both for sending and receiving messages.  After connection factory beans, we define a destination used as default destination. It's a  queue with name "Send2Recv". Finally there's the Spring JmsTemplate bean. It use the connection factory and defaultDestination bean we create above. If you don't define default destination for JmsTemplate, you will need to explicitly set destination when you try to send any message in your java code.

The Jms service provider's bean, namely ActiveMQ related beans,  will NOT be used in your code directly. There are the input of Spring JMS.  You code use beans from Spring JMS instead.

Everything is almost done now. Let's review the directory structure for our maven project.

image



5. Run the code


Before you can run the code, you need to make sure the ActiveMQ broker is running. So our jave code can connect to it and send message to it.   Make sure the broker IP and port are correct in your spring configuration file "app-context.xml". Run the ActiveMQ broker like with this command.
ACTIVEMQ_INSTALL_DIR/bin/activemq start

Now you can run you main class.  you can run it from IDE such as eclipse, or you can run it direct in command line by using maven.
# run this from project directory
cd spring-jms-activemq-send
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"

It should run quietly without any error. After running your main, you can check the result through ActiveMQ's administration tool. ActiveMQ can be managed by browser on port 8161.  My ActiveMQ broker is running on IP 192.168.203.143, so I can access URL http://192.168.203.143:8161/admin to browse all queues and all messages in the queues.

image As you can see after running the main, there are 2 queues created on the broker. One is named "Send2Recv" which we used as default destination, the other one is "AnotherDest" which we create in the main class. By click into the queue, all message be list out, click any message can see the detain of that message , including message header and message content.

image image

6. More


Here are more hello world level examples on how to receive message from JMS with Spring jms and ActiveMQ.

Monday, September 15, 2014

Play with Java byte array

Java byte variable is mostly used when you do some low level programming. Let's go though some FAQ you will meet when using Java byte array.
Here are some ASCII characters and their values that used in the following demo lines:

Character Value in hex Value in decimal
a 0x61 97
b 0x62 98
c 0x63 99
1 0x31 49
2 0x32 50
3 0x33 51
How to initial a byte array with values
// these two lines are same
byte[] byteArray = new byte[] {0x61,0x62,0x63,0x31,0x32,0x33};
byte[] byteArray = new byte[] {97,98,99,49,50,51};
 

one thing to note is that if value of a byte is >= 0x80 (128 in decimal), you need to add cast like the following demo:

// these two lines are same
byte[] byteArray = new byte[] {0x61,(byte)0x82,0x63};
byte[] byteArray = new byte[] {97,(byte)130,99};
 

How to convert a byte array to String
This is a very ambiguous question, so let's clarify it in more details.

Case 1 {0x61,0x62,0x63} -> "abc":
Suppose you have a byte array. You target is to get a string from the ASCII value of the byte array, which means you want to get a String looks like "abc"from byte array {0x61,0x62,0x63} (If you want the output looks like "61 62 63" or "0x61 0x62 0x63" instead, check case 2 here). Java class String provide us the ability for doing that.
// english String
byte[] byteArray = new byte[] {0x61,0x62,0x63,0x31,0x32,0x33};
String output = new String(byteArray);
Syste.out.println("output="+output);  // output=abc123

If you have byte value > 0x80 or maybe you are dealing with non-english character, you need to specify the character set in the String's constructor to get the right String output. Adding character set in String constructor will also need you to catch UnsupportedEncodingException.
// non-english String
try {
 byte[] byteArray = new byte[]{(byte) 0xd6,(byte) 0xd0,(byte) 0xce,(byte) 0xc4};
 String output = new String(byteArray,"GB18030");
 Syste.out.println("output="+output);  // output=中文
} catch (UnsupportedEncodingException e) {
 e.printStackTrace();
}

Case 2 {0x61,0x62,0x63} -> "61 62 63" or "0x61 0x62 0x63":
Suppose you have a byte array. You target is to get a hex value string, so you can print it to stdout or log file. This is quite useful in debug. I have done some search one the web, the best solution is from stackoverflow here, not the it's selected answer but the discuss below it, suggesting javax.xml.bind.DatatypeConverter.printHexBinary(byte[] byteArray). It saves you from writing any thing like a loop to read the byte from array one by one. It comes with Java itself and faster than any customerized code.

The output of DatatypeConverter.printHexBinary(byte[] byteArray) is a string without space separator. For example a intput byte array {0x61,0x62,0x63} will get you "616263". We can then beautify the output of DatatypeConverter.printHexBinary(byte[] byteArray) with a regular expression to add space to every 2 characters.


import javax.xml.bind.DatatypeConverter;

byte[] byteArray = new byte[] {0x61,0x62,0x63,0x31,0x32,0x33};
String hexString = DatatypeConverter.printHexBinary(byteArray).replaceAll("([0-9a-fA-F]{2})", "$1 ").trim();
System.out.println(hexString);  // 61 62 63 31 32 33

You man have noticed there is one space after the regular expression match group variable $1. After the replaceAll function you have the String "61 62 63 31 32 33 ". The last match will create a space at the end of the string. The trim() will remove the tailing space nicely.

If you need a more readable String like "0x61 0x62" with "0x" prefix for each hex value, you can tweak regular expression part, change "$1 " to "0x$1 ".
byte[] byteArray = new byte[] {0x61,0x62,0x63,0x31,0x32,0x33};
String hexString = DatatypeConverter.printHexBinary(byteArray).replaceAll("([0-9a-fA-F]{2})", "0x$1 ").trim();
System.out.println(hexString);  // 0x61 0x62 0x63 0x31 0x32 0x33

How to convert a String to byte array
Like the previous one, this is also a ambiguous question.

Case 1 "a1b2" -> {0x61,0x31,0x62,0x32}:
Suppose you have a String, you want to get every character's ASCII value. This is how the method getBytes() of String class does.
  String str = "a1b2";
  byte[] byteArray = str.getBytes();     // byteArray = {0x61,0x31,0x62,0x32}
  System.out.println(byteArray.length);  // 4
Also like the previous question case 1 on convert byte array to String, If you have any non-english character in the String, character set is needed for getBytes() method. The getBytes(String characterSet) can throw UnsupportedEncodingException exception, so you have to catch it in your code.
  
  String str = "中文";
  byte[] byteArray;
  try {
   byteArray = str.getBytes("GB18030");   // byteArray = {0xD6,0xD0,0xCE,0xC4}
   System.out.println(byteArray.length);  // 4
   logger.info(Utils.byteArrayToString(byteArray));
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  } 

Case 2 "a1b2" -> {0xa1,0xb2}:
Suppose you have String as a hex String. Every 2 characters in the string stand for a byte. You can use javax.xml.bind.DatatypeConverter.parseHexBinary(String str) to achieve your goal gracefully.

There are also many code snippets on the web for the same purpose, but I think the DatatypeConverter.parseHexBinary(String str) is the best way to do so. It's a one-line solution provided by Java itself, simple and clean.
 
  import javax.xml.bind.DatatypeConverter;
 
  String str = "a1b2";
  byte[] byteArray = DatatypeConverter.parseHexBinary(str); // byteArray={0xa1,0xb2}


Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List