Java EE Development With Eclipse - Second Edition - Sample Chapter
Java EE Development With Eclipse - Second Edition - Sample Chapter
Second Edition
$ 59.99 US
38.99 UK
P U B L I S H I N G
Sa
m
pl
C o m m u n i t y
E x p e r i e n c e
D i s t i l l e d
Second Edition
Java EE Development
with Eclipse
ee
Second Edition
Develop, debug, test, and troubleshoot Java EE 7 applications
rapidly with Eclipse
Ram Kulkarni
Preface
Java 2 Enterprise Edition (J2EE) has been used to develop enterprise applications
for many years. It provides a standard technique to implement the many aspects
of an enterprise application, such as handling web requests, accessing database,
connecting to other enterprise systems, and implementing web services. Over the
years, it has evolved and made enterprise application development easier than
before. Its name has changed as well, from J2EE to JEE, after the J2EE version 1.4.
Currently, it is in version 7.
Eclipse is a popular Integrated Development Environment (IDE) for developing
Java applications. It has a version specific to the JEE development too, which makes
it faster to write code and easier to deploy JEE applications on a server. It provides
excellent debugging and unit testing support. Eclipse has a modular architecture,
and many plugins are available today to extend its functionality for performing
many different tasks.
This book provides you with all the information that you will need to use Eclipse
to develop, deploy, debug, and test JEE applications. The focus of this book is to
provide you with practical examples of how to develop applications using JEE
and Eclipse. The scope of this book is not limited to JEE technologies, but covers
other technologies used in the different phases of application development as well,
such as source control, unit testing, and profiling.
JEE is a collection of many technologies and specifications. Some of the technologies
are so vast that separate books will have to be written on them and many have
been already written. This book takes the approach of providing you with a brief
introduction to each technology in JEE and provides links for detailed information.
Then it moves on to develop sample applications using specific technologies under
discussion and explains the finer aspects of the technologies in the context of the
sample applications.
Preface
This book could be useful to you if you are new to JEE and want to get started with
developing JEE applications quickly. You will also find this book useful if you are
familiar with JEE but looking for hands-on approach to use some of the technologies
in JEE.
Preface
Chapter 10, Asynchronous Programming with JMS, shows explains how to write
applications to process messages asynchronously. It describes how to program
queues and topics of messaging systems using JMS and MDBs.
Chapter 11, Java CPU Profiling and Memory Tracking, describes the techniques for
profiling CPU and memory in Java applications to find performance bottlenecks.
Chapter 10
Asynchronous Programming
with JMS
Thus far, we have seen examples of clients making requests to the JEE server
and waiting till the server sends a response back. This is a synchronous model of
programming. This model of programming may not be suitable when the server
takes a long time to process requests. In such cases, a client might want to send a
request to the server and return it immediately without waiting for the response.
The server would process the request and somehow make the result available to the
client. Requests and responses in such scenarios are sent through messages. Further,
there is a message broker that makes sure that messages are sent to the appropriate
recipients. This is also known as message-oriented architecture. The following are
some of the advantages of adopting the message-oriented architecture:
[ 357 ]
There are many enterprise messaging systems, such as Apache ActiveMQ (http://
activemq.apache.org/), RabbitMQ (https://www.rabbitmq.com/), and MSMQ
(https://msdn.microsoft.com/en-us/library/ms711472(v=vs.85).aspx).
Further, the JMS (which stands for Java messaging service) specification provides a
uniform interface to work with many different messaging systems. JMS is also a part
of the overall Java EE specifications. Refer to http://docs.oracle.com/javaee/7/
tutorial/jms-concepts.htm#BNCDQ for an overview of JMS APIs.
There are two types of message containers in messaging systems:
In this chapter, we will see how to use JMS APIs for sending and receiving messages.
We will use a GlassFish server, which also has a built-in JMS provider. We will use
JMS APIs to implement a use case in the Course Management application, the same
application that we have been building in the other chapters of this book.
[ 358 ]
Chapter 10
Create a sender:
QueueSender sender = session.createSender(queue);
Create a receiver:
//create a new session before creating the receiver.
QueueReceiver receiver = session.createReceiver(queue);
[ 359 ]
Or:
Message message = receiver.receive(timeout);
Or:
Message message = receiver.receiveNoWait(); //returns null
if no message is available.
Some of the steps can be skipped when JMS annotations are used or when MDBs are
used to receive messages. We will see examples of these later.
Now, let's create a working example of sending and receiving messages using JMS.
Make sure that you have installed the GlassFish application server (refer to the
Installing the GlassFish server section in Chapter 1, Introducing JEE and Eclipse) and
configured it in Eclipse JEE (refer to the Configuring the GlassFish server in Eclipse
section in Chapter 7, Creating JEE Applications with EJB). The use case that we will
implement in this example is adding a new course. Although this is not a strong use
case for asynchronous processing, we will assume that this operation takes a long
time and needs to be handled asynchronously.
[ 360 ]
Chapter 10
[ 361 ]
[ 362 ]
Chapter 10
[ 363 ]
Let's start with the first example that uses JSP, bean, and JMS APIs. Create a web
project by selecting File | New | Dynamic Web Project.
Chapter 10
[ 365 ]
We will implement the code to send the CourseDTO object to the JMS queue that we
have configured in the addCourse method later. For now, add the following code to
addCourse.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF8">
<title>Add Course</title>
</head>
<body>
<!-- Check if form is posted -->
<c:if test="${\"POST\".equalsIgnoreCase(pageContext.request.method)
&& pageContext.request.getParameter(\"submit\") != null}">
<!-- Create CourseJSPBean -->
<jsp:useBean id="courseService" class="packt.jee.eclipse.jms.jsp_
beans.CourseJSPBean"
scope="page"></jsp:useBean>
<!-- Set Bean properties with values from form submission -->
<jsp:setProperty property="name" name="courseService"
param="course_name"/>
<jsp:setProperty property="credits" name="courseService"
param="course_credits"/>
<!-- Call addCourse method of the bean -->
[ 366 ]
Chapter 10
${courseService.addCourse()}
<b>Course detailed are sent to a JMS Queue. It will be
processed later</b>
</c:if>
<h2>New Course:</h2>
<!-- Course data input form -->
<form method="post">
<table>
<tr>
<td>Name:</td>
<td>
<input type="text" name="course_name">
</td>
</tr>
<tr>
<td>Credits:</td>
<td>
<input type="text" name="course_credits">
</td>
</tr>
<tr>
<td colspan="2">
<button type="submit" name="submit">Add</button>
</td>
</tr>
</table>
</form>
</body>
</html>
At the top of the JSP file, we check whether the form is submitted. If yes, then we
create an instance of CourseJSPBean and set its properties with values from the form
submission. Then, we call the addCourse method of the bean.
[ 367 ]
Executing addCourse.jsp
We still haven't added any code to put the Course object in the JMS queue.
However, if you want to test the JSP and bean, add the project to the GlassFish server
configured in Eclipse. To do this, right-click on the configured server in the Servers
view of Eclipse and select the Add Remove option. Select the web project that we
created above and click Finish. Make sure that the server is started and the status is
[Started, Synchronized].
If the status is Republish, then right-click on the server and select the Publish
option. If the status is Restart, right-click on the server and select the Restart option.
You may not have to do this immediately after adding a project, but later when we
make changes to the code, you may have to republish or restart the server or both.
So, keep a watch on the server status before you execute the code in Eclipse.
To execute addCourse.jsp, right-click on the file in either Project Explorer or the
editor, and select the Run As | Run on Server option. This will open the built-in
Eclipse browser and open JSP in it. You should see the form for adding the course
details. If you click the Submit button, you should see the message that we added in
JSP when the form is submitted.
Let's now add a class to send the course details to the JMS queue.
Chapter 10
//Create JMS Connection, session, and queue objects
InitialContext initCtx = new InitialContext();
QueueConnectionFactory connectionFactory =
(QueueConnectionFactory)initCtx.
lookup("jms/CourseManagemenCF");
connection = connectionFactory.createQueueConnection();
connection.start();
session = connection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queue = (Queue)initCtx.lookup("jms/courseManagementQueue");
}
public void close() {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
@Override
protected void finalize() throws Throwable {
close(); //clean up
super.finalize();
}
public void sendAddCourseMessage (CourseDTO course) throws
Exception {
//Send CourseDTO object to JMS Queue
QueueSender sender = session.createSender(queue);
ObjectMessage objMessage =
session.createObjectMessage(course);
sender.send(objMessage);
}
}
In the constructor, we look up the JMS connection factory and create the connection.
We then create a JMS session and look up queue with the JNDI name that we used
for creating the queue in a previous section.
[ 369 ]
Note that we did not specify any configuration properties when constructing
InitialContext. This is because the code is executed in the same instance of the
GlassFish server that hosts the JMS provider. If you are connecting to a JMS provider
hosted in a different GlassFish server, then you will have to specify the configuration
properties, particularly for the remote host. For example:
Properties jndiProperties = new Properties();
jndiProperties.setProperty("org.omg.CORBA.ORBInitialHost",
"<remote_host>");
//target ORB port. default is 3700 in Glassfish
jndiProperties.setProperty("org.omg.CORBA.ORBInitialPort",
"3700");
InitialContext ctx = new InitialContext(jndiProperties);
[ 370 ]
Chapter 10
session.setAttribute("CourseQueueSender",
courseQueueSender);
}
//TODO: perform input validation
if (courseQueueSender != null) {
try {
courseQueueSender.sendAddCourseMessage(course);
} catch (Exception e) {
e.printStackTrace();
//TODO: log exception
}
}
}
If we don't find the CourseQueueSender object in the session, then we will create one
and save it in the session.
We need to modify the call to the addCourse method from addcourse.
jsp. Currently, we do not pass any argument to the method. However,
with the preceding changes to the addCourse method, we need to pass the
HttpServletRequest object to it. JSP has a build-in property called pageContext
that provides access to the HttpServletRequest object. So, modify the code in
addCourse.jsp where courseService.addCourse is called as follows:
<!-- Call addCourse method of the bean -->
${courseService.addCourse(pageContext.request)}
We can test our code at this point, but although a message is sent to the queue, we
haven't implemented any consumer to receive a message from the queue. So, let's
implement a JMS queue consumer for our Course queue.
Chapter 10
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
//TODO: log exception
}
}
}
}
The code to look up the connection factory and the queue is similar to that in
CourseQueueSender. Note that the constructor takes a name argument. We don't
really need to use a JMS API, but we will use it as an identifier for instances of the
CourseQueueReceiver class. We register a message listener in the constructor, and
in the onMessage method of the listener class, we get the CourseDTO object from the
message and print the message to the console. This message will appear in GlassFish
console in Eclipse when we execute the code. To keep the example simple, we have
not implemented the code to save the Course information to the database, but you
can do so by using JDBC or JDO APIs that we have already learnt in Chapter 4,
Creating a JEE Database Application.
We need to instantiate this class at the application startup so that it will start listening
for messages. One way to implement this is in a Servlet that loads on startup.
Create the JMSReceiverInitServlet class in the packt.jee.eclipse.jms.
servlet package. We will mark this Servlet to load at startup by using annotations
and instantiate CourseQueueReceiver in the init method.
package packt.jee.eclipse.jms.servlet;
//skipped imports
@WebServlet(urlPatterns="/JMSReceiverInitServlet",
loadOnStartup=1)
public class JMSReceiverInitServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private CourseQueueReceiver courseQueueReceiver = null;
public JMSReceiverInitServlet() {
super();
}
@Override
public void init(ServletConfig config) throws ServletException
{
[ 373 ]
Publish the project again in the server and execute addCourse.jsp (see the Executing
addCourse.jsp section). Switch to the Console view in Eclipse. You should see the
message that we printed in the onMessage method in CourseQueueReceiver.
Chapter 10
private CourseQueueReceiver courseQueueReceiver1 = null;
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
try {
//first instance of CourseQueueReceiver
courseQueueReceiver = new CourseQueueReceiver("Receiver1");
//create another instance of CourseQueueReceiver with a
different name
courseQueueReceiver1 = new CourseQueueReceiver("Receiver2");
} catch (Exception e) {
log("Error creating CourseQueueReceiver", e);
}
}
@Override
public void destroy() {
if (courseQueueReceiver != null)
courseQueueReceiver.stop();
if (courseQueueReceiver1 != null)
courseQueueReceiver1.stop();
super.destroy();
}
//rest of the code remains the same
}
Republish the project, execute addCourse.jsp, and add a few courses. Check
the Console messages. You may see that some of the messages were received by
Receiver1 and the others by Receiver2.
Figure 10.7 Console output showing multiple JMS receivers listening to a JMS queue
[ 375 ]
[ 376 ]
Chapter 10
}
}
}
public void publishAddCourseMessage (CourseDTO course) throws
Exception {
TopicPublisher sender = session.createPublisher(topic);
ObjectMessage objMessage =
session.createObjectMessage(course);
sender.send(objMessage);
}
}
The code is quite simple and self-explanatory. Let's now modify the queue receiver
class that we implemented, CourseQueueReceiver, to publish a message to the
topic from the onMessage method, after the message from the queue is handled
successfully.
public class CourseQueueReceiver {
private CourseTopicPublisher topicPublisher;
public CourseQueueReceiver(String name) throws Exception{
//code to lookup connection factory, create session,
//and look up queue remains unchanged. Skipping this code
//create topic publisher
topicPublisher = new CourseTopicPublisher();
QueueReceiver receiver = session.createReceiver(queue);
//register message listener
receiver.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
//we expect ObjectMessage here; of type CourseDTO
//Skipping validation
try {
//code to process message is unchanged. Skipping it
//publish message to topic
[ 377 ]
[ 378 ]
Chapter 10
subscriber.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
//we expect ObjectMessage here; of type CourseDTO
//skipping validation
try {
CourseDTO course = (CourseDTO)
((ObjectMessage)message).getObject();
//process addCourse action. For example, save it in
database
System.out.println("Received addCourse notification for
Course name - "
+ course.getName() + " in Subscriber " +
subscriberName);
} catch (JMSException e) {
e.printStackTrace();
//TODO: handle and log exception
}
}
});
}
public void stop() {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
//TODO: log exception
}
}
}
}
[ 379 ]
topic as long as the subscriber is active. If the subscriber becomes inactive and then
active again, it loses messages published by the topic during that period. If you
want to make sure that the subscriber receives all messages, you need to create a
durable subscription using TopicSession.createDurableSubscriber. Along with
the topic name, this method takes the subscriber name as the second argument.
Refer to https://docs.oracle.com/javaee/7/api/javax/jms/TopicSession.
html#createDurableSubscriber-javax.jms.Topic-java.lang.String- for
more information.
We will create two instances of this class (so there would be two topic subscribers)
in JMSReceiverInitServlet, so that subscribers start listening for messages on the
application start (the Servlet is loaded on startup).
@WebServlet(urlPatterns="/JMSReceiverInitServlet",
loadOnStartup=1)
public class JMSReceiverInitServlet extends HttpServlet {
private CourseQueueReceiver courseQueueReceiver = null;
private CourseTopicSubscriber courseTopicSubscriber = null;
private CourseQueueReceiver courseQueueReceiver1 = null;
private CourseTopicSubscriber courseTopicSubscriber1 = null;
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
try {
courseQueueReceiver = new CourseQueueReceiver("Receiver1");
courseQueueReceiver1 = new CourseQueueReceiver("Receiver2");
courseTopicSubscriber = new
CourseTopicSubscriber("Subscriber1");
courseTopicSubscriber1 = new
CourseTopicSubscriber("Subscriber2");
} catch (Exception e) {
log("Error creating CourseQueueReceiver", e);
}
}
//remaining code is unchanged. Skipping it
}
[ 380 ]
Chapter 10
Therefore, now, we have two queue listeners and two topic listeners ready when the
application starts. Republish the project, execute addCourse.jsp, and add a course.
Check messages in the Console view of Eclipse. You will see that the message
published in the topic is received by all subscribers, but the same message published
in queue is received by only one receiver.
Figure 10.8 Console output showing multiple JMS receivers listening to JMS queue and topic
[ 381 ]
We will first create three managed beans for JSF. The first one is
CourseManagedMsgSenderBean. The second one is CourseManagedMsgReceiverBean,
and the last one is CourseJSFBean, which will be referenced from the JSF page.
Create the CourseManagedMsgSenderBean class in the packt.jee.eclipse.jms.
jsf_bean package:
package packt.jee.eclipse.jms.jsf_bean;
//skipped imports
@ManagedBean(name="courseMessageSender")
@SessionScoped
public class CourseManagedMsgSenderBean {
@Resource(name = "jms/CourseManagemenCF")
private QueueConnectionFactory connectionFactory;
@Resource(lookup = "jms/courseManagementQueue")
private Queue queue;
QueueConnection connection;
QueueSession session;
Exception initException = null;
@PostConstruct
public void init() {
try {
connection = connectionFactory.createQueueConnection();
connection.start();
session = connection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
} catch (Exception e) {
initException = e;
}
}
@PreDestroy
[ 382 ]
Chapter 10
public void cleanup() {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
//TODO: log exception
}
}
}
public void addCourse(CourseDTO courseDTO) throws Exception {
if (initException != null)
throw initException;
QueueSender sender = session.createSender(queue);
ObjectMessage objMessage =
session.createObjectMessage(courseDTO);
sender.send(objMessage);
}
}
Notice that the JMS connection factory and queue objects are injected using the @
Resource annotation. We have used the @PostConstruct annotation to create a JMS
connection and session and the @PreDestroy annotation for a clean-up operation.
The addCourse method is similar to the code that we have already implemented in
the CourseQueueSender class in a previous section.
Now, create a JMS message receiver class. Create the
CourseManagedMsgReceiverBean class in the packt.jee.eclipse.jms.jsf_bean
package.
package packt.jee.eclipse.jms.jsf_bean;
//skipped imports
@ManagedBean(name="courseMessageReceiver")
@ApplicationScoped
public class CourseManagedMsgReceiverBean {
@Resource(name = "jms/CourseManagemenCF")
private QueueConnectionFactory connectionFactory;
[ 383 ]
In this class also, JMS resources are injected using the @Resource tags. The @
PostConstruct method creates a connection, session, and a receiver. It also registers
MessageListener. The code is similar to what we wrote in the constructor of
CourseQueueReceiver, so some of the code is skipped in the previous listing. Please
download the source code for this chapter to see the complete source code.
Chapter 10
//instantiate CourseManagedMsgReceiverBean and start
//message listener
context.getApplication().evaluateExpressionGet(context,
"#{courseMessageReceiver}",
CourseManagedMsgReceiverBean.class);
}
Form fields are bound to fields in CourseJSFBean. When the Submit button is
clicked, the addCourse method of the same bean is called, which puts a message in
the JMS queue.
Republish the project and execute addCourse.xhtml by right-clicking it and
selecting Run As | Run on Server. Add a course and see the message printed (from
MessageListener in CourseManagedMsgReceiverBean) in the GlassFish Console
view of Eclipse.
[ 386 ]
Chapter 10
[ 387 ]
Enter Project name as CourseManagementEJB. Click Next. Accept the default values
on the subsequent pages, and click Finish on the last page.
Right-click on the project, and select the New | Message-Driven Bean option. This
opens the MDB creation wizard.
[ 388 ]
Chapter 10
Figure 10.11 JMS queue physical destination name in GlassFish admin console
[ 389 ]
Summary
Messaging systems can be powerful tools for integrating disparate applications.
They provide an asynchronous model of programming. The client does not wait for
the response from the server and the server does not necessarily process requests at
the same time that the client sends them. Messaging systems can also be useful for
building scalable applications and batch processing. JMS provides uniform APIs to
access different messaging systems.
In this chapter, we discussed how to send and receive messages from queue and
to publish and subscribe messages from topic. There are many different ways to
use JMS APIs. We started with the basic JMS APIs and then, discussed how
annotations can help reduce some of the code. We also discussed how to use
MDBs to consume messages.
In the next chapter, we will see some of the techniques and tools used for profiling
the CPU and memory usage in Java applications.
[ 390 ]
www.PacktPub.com
Stay Connected: