Java EE Notes For Professionals
Java EE Notes For Professionals
Java EE
Notes for Professionals
®
20+ pages
of professional hints and tricks
Disclaimer
GoalKicker.com This is an unocial free book created for educational purposes and is
not aliated with ocial Java® EE group(s) or company(s).
Free Programming Books All trademarks and registered trademarks are
the property of their respective owners
Contents
About ................................................................................................................................................................................... 1
Chapter 1: Getting started with Java EE ........................................................................................................... 2
Section 1.1: What is Java EE? ........................................................................................................................................ 2
Section 1.2: Installation .................................................................................................................................................. 3
Section 1.3: Installing Payara Server Full ..................................................................................................................... 3
Section 1.4: Building my First JavaEE Application (Hello World) .............................................................................. 4
Chapter 2: Java RESTful Web Services (JAX-RS) ......................................................................................... 6
Section 2.1: Simple Resource ........................................................................................................................................ 6
Section 2.2: GET method types .................................................................................................................................... 6
Section 2.3: POST Method ............................................................................................................................................ 7
Section 2.4: Name binding ............................................................................................................................................ 8
Section 2.5: Exception Mapper ..................................................................................................................................... 9
Section 2.6: DELETE method ........................................................................................................................................ 9
Section 2.7: Custom parameter converters .............................................................................................................. 10
Section 2.8: SubResources ......................................................................................................................................... 11
Section 2.9: UriInfo ...................................................................................................................................................... 11
Chapter 3: The WebSockets API .......................................................................................................................... 13
Section 3.1: Creating a WebSocket communication ................................................................................................ 13
Section 3.2: Encoders and Decoder: Object-Oriented WebSockets ...................................................................... 14
Chapter 4: Java Messaging Service (JMS) .................................................................................................... 21
Section 4.1: Using ActiveMQ library for messaging (activemq jms provider specific implementations)
................................................................................................................................................................................ 21
Section 4.2: Creating ConnectionFactory ................................................................................................................. 23
Section 4.3: Using jndi based lookup for messaging (Non-implementation-specific example) ......................... 24
Chapter 5: Java Connector Architecture (JCA) ........................................................................................... 26
Section 5.1: Example Resource Adapter .................................................................................................................... 26
Chapter 6: The Javamail API ................................................................................................................................. 27
Section 6.1: Send HTML Formatted Mail .................................................................................................................... 27
Section 6.2: Send Simple Email .................................................................................................................................. 27
Credits .............................................................................................................................................................................. 29
You may also like ........................................................................................................................................................ 30
About
Please feel free to share this PDF with anyone for free,
latest version of this book can be downloaded from:
http://GoalKicker.com/JavaEEBook
This Java® EE Notes for Professionals book is compiled from Stack Overflow
Documentation, the content is written by the beautiful people at Stack Overflow.
Text content is released under Creative Commons BY-SA, see credits at the end
of this book whom contributed to the various chapters. Images may be copyright
of their respective owners unless otherwise specified
This is an unofficial free book created for educational purposes and is not
affiliated with official Java® EE group(s) or company(s) nor Stack Overflow. All
trademarks and registered trademarks are the property of their respective
company owners
A key concept of the Java EE is that every Java EE version is comprised by a set of specific technologies. These
technologies address specific JSRs (Java Specification Requests). In order for a programmer to use these
technologies he needs to download an implementation of the Java EE technology specifications. The Java
Community provides a reference implementation for each technology but other Java EE compliant technologies are
developed and can also be used. The community provides a set of tests, namely the Java Compatibility Kit (JCK) that
can be used by the developers of a JSR implementation to check if it is compliant or not with the JSR. The following
table gives an overview of the technologies that comprise Java EE 7 and the related JSR that define the specs.
Depending on your needs, there are lots of possibilities. To install most (or all) of the specifications, you can choose
a Java EE 7 compatible Application Server. Depending on your needs, you can choose between application servers
that implement the web profile or application servers that implement the full profile. For a list of Java EE7
compatible application servers see Java EE Compatibility.
JDK 1.7 or later installed. You can find the Oracle JDK's here.
Steps
Windows: Open a command prompt and execute the following command to start/stop Payara:
"C:\payara41\bin\asadmin" start-domain
"C:\payara41\bin\asadmin" stop-domain
Linux/Max: Open a terminal and execute the following command to start/stop Payara:
/payara41/bin/asadmin start-domain
/payara41/bin/asadmin stop-domain
To check that you're running the Application Server, open a browser and go to http://localhost:4848 to see the
Payara Server Console.
Voila! Now it's time to build your first application using JavaEE and deploy it to your server!
So as you might guess, there's not only one way to build a simple Hello World application in JavaEE.
Secondly, the JavaEE spec has a specific structure of folders that looks something like this (simplified):
/projectname/src/main/java
/projectname/src/main/resources
/projectname/src/main/resources/META-INF
/projectname/src/main/webapp
/projectname/src/main/webapp/WEB-INF
Inside the /projectname/src/main/java we put all the java classes that we need.
Inside the /projectname/src/main/webapp we put html files, css files, javascript files, etc.
Inside the /projectname/src/main/webapp/WEB-INFgoes some optional configuration files, such as web.xml and
beans.xml.
For simplicity we will use the NetBeans IDE (it's free) to build our first JavaEE Application. You can find it here.
Choose the JavaEE version and install it.
View results
In a browser, go to http://localhost:8080/HelloJavaEE-1.0-SNAPSHOT
Voila! That's your first app using JavaEE technology. You should now start creating other "Hello World" apps using
different specifications like JPA, EJB, JAX-RS, JavaBatch, etc...
@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {
/**
* JAX-RS root path.
*/
public static final String ROOT_PATH = "/api";
Resources are simple POJO classes which are annotated with the @Path annotation.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("/hello")
public class HelloWorldResource {
public static final String MESSAGE = "Hello StackOverflow!";
@GET
@Produces("text/plain")
public String getHello() {
return MESSAGE;
}
}
When a HTTP GET request is sent to /hello, the resource responds with a Hello StackOverflow! message.
@Path("/hello")
public class HelloWorldResource {
public static final String MESSAGE = "Hello World!";
@GET
@Produces("text/plain")
public String getHello() {
return MESSAGE;
}
@GET
@Path("/{letter}")
@Produces("text/plain")
GET without a parameter gives all content ("Hello World!") and GET with path parameter gives the specific letter out
of that String.
Some examples:
Note: if you leave out the method-type annotation (e.g. the @GET above), a request method defaults to being a GET
request handler.
@Path("hello")
public class HelloWorldResource {
@POST
@Path("/receiveParams")
public Response receiveHello(@FormParam("name") String name, @FormParam("message") String
message) {
//process parameters
return Response.status(200).build();
}
@POST
@Path("/saveObject")
@Consumes("application/json")
public Response saveMessage(Message message) {
//process message json
return Response.status(200).entity("OK").build();
}
}
First method can be invoked through HTML form submission by sending captured input parameters. Form submit
action should point to -
/hello/receiveParams
Second method requires Message POJO with getters/setters. Any REST client can call this method with JSON input
as -
{"sender":"someone","message":"Hello SO!"}
POJO should have the same property as JSON to make serialization work.
Filters or interceptors can be assigned to a resource method using the @NameBinding annotation. This annotation is
used as meta annotation for other user implemented annotations that are applied to a providers and resource
methods. See the following example:
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}
The example above defines a new @Compress annotation which is a name binding annotation as it is annotated with
@NameBinding. The @Compress annotation can be used to bind filters and interceptor to endpoints.
Consider you have an interceptor that performs GZIP compression and you want to bind such interceptor to a
resource method. To do it, annotate both the resource method and the interceptor, as following:
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new GZIPOutputStream(outputStream));
context.proceed();
}
}
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
@GET
@Path("too-much-data")
@Compress
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
The @Compress is applied on the resource method getVeryLongString() and on the interceptor
GZIPWriterInterceptor. The interceptor will be executed only if any resource method with such a annotation will
be executed.
In above example, the interceptor will be executed only for the getVeryLongString() method. The interceptor will
not be executed for method getHello(). In this example the reason is probably clear. We would like to compress
only long data and we do not need to compress the short response of "Hello World!".
Name binding can be applied on a resource class. In the example HelloWorldResource would be annotated with
@Compress. This would mean that all resource methods will use compression in this case.
Note that global filters are executed always, so even for resource methods which have any name binding
annotations.
Documentation
@Override
public Response toResponse(IllegalArgumentException exception) {
return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
}
}
This exception mapper will catch all IllegalArgumentExceptions thrown in the application, and show the user a clear
message instead of a stacktrace.
@Path("hello")
public class HelloWorldResource {
@DELETE
public Response deleteMessage() {
message = null;
return Response.noContent().build();
}
}
$ curl http://localhost/hello
Hello StackOverflow!
$ curl http://localhost/hello
null
@Provider
public class ParamConverters implements ParamConverterProvider {
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType,
Type genericType,
Annotation[] annotations)
{
if (rawType == LocalDate.class)
return (ParamConverter<T>) new ParamConverter<LocalDate>() {
@Override
public LocalDate fromString(String value) {
return LocalDate.parse(value);
}
@Override
public String toString(LocalDate value) {
return null;
}
};
else if (rawType == MonthDay.class)
return (ParamConverter<T>) new ParamConverter<MonthDay>() {
@Override
public MonthDay fromString(String value) {
int[] ddmm = Arrays.stream(value.split("/"))
.mapToInt(Integer::parseInt)
.toArray();
return MonthDay.of(ddmm[1], ddmm[0]);
}
@Override
public String toString(MonthDay value) {
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("items")
public class ItemsResource {
@Path("{id}")
public String item(@PathParam("id") String id) {
return new ItemSubResource(id);
}
@GET
@Produces("text/plain")
public Item item() {
return "The item " + id;
}
}
}
@GET
@Path("resource")
@Produces(MediaType.TEXT_PLAIN)
public Response getResource(@Context UriInfo uriInfo) {
StringBuilder sb = new StringBuilder();
sb.append("Path: " + uriInfo.getPath() + "\n");
sb.append("Absolute Path: " + uriInfo.getAbsolutePath() + "\n");
sb.append("Base URI: " + uriInfo.getBaseUri() + "\n");
Path: class/resource
Absolute Path: https://localhost:8080/webapp/servlet/class/resource#
Base URI: https://localhost:8080/webapp/servlet/
Request URI: https://localhost:8080/webapp/servlet/class/resource
the client opens a connection to a server that is listening for a WebSocket request
a client connects to a server using a URI.
A server may listen to requests from multiple clients.
Server Endpoint
You can create a WebSocket server entpoint by just annotate a POJO with @ServerEndpoint. @OnMessage decorates
a method that receives incoming messages. @OnOpen can be used to decorate a method to be called when a new
connection from a peer is received. Similarly, a method annotated with @OnClose is called when a connection is
closed.
@ServerEndpoint("/websocket")
public class WebSocketServerEndpoint
{
@OnOpen
public void open(Session session) {
System.out.println("a client connected");
}
@OnClose
public void close(Session session) {
System.out.println("a client disconnected");
}
@OnMessage
public void handleMessage(String message) {
System.out.println("received a message from a websocket client! " + message);
}
Client Endpoint
Similar to the server endpoint you can create a WebSocket client endpoint by annotate a POJO with
@ClientEndpoint.
@ClientEndpoint
public class WebsocketClientEndpoint {
@OnOpen
public void onOpen(Session userSession) {
System.out.println("opening websocket");
@OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing websocket");
this.userSession = null;
}
@OnMessage
public void onMessage(String message) {
System.out.println("received message: "+ message);
}
Messages definition
Let's assume all received messages have to be transformed by the server before being sent back to all connected
sessions:
Let's now assume that the server manage two message types: a text-based message and an integer-based
message.
@Override
public void transform() {
this.content = this.content * this.content;
}
@Override
public void transform() {
this.content = "Someone said: " + this.content;
}
}
There is one encoder per message type and a single decoder for all messages. Encoders must implements
Encoder.XXX<Type> interface when Decoder must implements Decoder.XXX<Type>.
Encoding is fairly straightforward: from a message, the encode method must output a JSON formatted String. Here
is the example for IntegerMsg.
@Override
public String encode(IntegerMsg object) throws EncodeException {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("content", object.getContent());
@Override
public void init(EndpointConfig config) {
System.out.println("IntegerMsgEncoder initializing");
}
@Override
public void destroy() {
System.out.println("IntegerMsgEncoder closing");
}
}
Similar encoding for StringMsg class. Obviously, encoders can be factorized via abstract classes.
@Override
public String encode(StringMsg object) throws EncodeException {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("content", object.getContent());
@Override
public void init(EndpointConfig config) {
System.out.println("StringMsgEncoder initializing");
}
@Override
public void destroy() {
System.out.println("StringMsgEncoder closing");
}
Decoder proceeds in two steps: checking if the received message fits the excepted format with willDecode and
then transform the received raw message into a object with decode:
@Override
public AbstractMsg decode(String s) throws DecodeException {
// Thanks to willDecode(s), one knows that
// s is a valid JSON and has the attribute
// "content"
JsonObject json = Json.createReader(new StringReader(s)).readObject();
JsonValue contentValue = json.get("content");
@Override
public boolean willDecode(String s) {
@Override
public void init(EndpointConfig config) {
System.out.println("Decoding incoming message...");
}
@Override
public void destroy() {
System.out.println("Incoming message decoding finished");
}
ServerEndPoint
The Server EndPoint pretty looks like the WebSocket communication with three main differences:
3. OnError annotation is used. If there was an error thrown during willDecode, it will be processed here and
error information is sent back to the client
@OnOpen
public void onOpen(Session session) {
System.out.println("A session has joined");
}
@OnClose
public void onClose(Session session) {
System.out.println("A session has left");
}
@OnMessage
public void onMessage(Session session, AbstractMsg message) {
if (message instanceof IntegerMsg) {
System.out.println("IntegerMsg received!");
} else if (message instanceof StringMsg) {
System.out.println("StringMsg received!");
}
message.transform();
@OnError
public void onError(Session session, Throwable throwable) {
session.getAsyncRemote().sendText(throwable.getLocalizedMessage());
}
As I was quite verbose, here is a basic JavaScript client for those who want to have a visual example. Please note
that this is a chat-like example: all the connected parties will received the answer.
<!DOCTYPE html>
<html>
<head>
<title>Websocket-object</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- start of BAD PRACTICE! all style and script must go into a
dedicated CSS / JavaScript file-->
<style>
body{
background: dimgray;
}
.container{
width: 100%;
display: flex;
}
.left-side{
width: 30%;
padding: 2%;
box-sizing: border-box;
margin: auto;
margin-top: 0;
background: antiquewhite;
}
.left-side table{
width: 100%;
border: 1px solid black;
margin: 5px;
}
.left-side table td{
padding: 2px;
width: 50%;
}
.left-side table input{
width: 100%;
box-sizing: border-box;
}
.right-side{
width: 70%;
<script>
var ws = null;
window.onload = function () {
// replace the 'websocket-object' with the
// context root of your web application.
ws = new WebSocket("ws://localhost:8080/websocket-object/webSocketObjectEndPoint");
ws.onopen = onOpen;
ws.onclose = onClose;
ws.onmessage = onMessage;
};
function onOpen() {
printText("", "connected to server");
}
function onClose() {
printText("", "disconnected from server");
}
function onMessage(event) {
var msg = JSON.parse(event.data);
printText("server", JSON.stringify(msg.content));
}
function sendNumberMessage() {
var content = new Number(document.getElementById("inputNumber").value);
var json = {content: content};
ws.send(JSON.stringify(json));
printText("client", JSON.stringify(json));
}
function sendTextMessage() {
var content = document.getElementById("inputText").value;
var json = {content: content};
ws.send(JSON.stringify(json));
printText("client", JSON.stringify(json));
}
switch (sender) {
case "client":
row.style.color = "orange";
break;
case "server":
row.style.color = "green";
break;
default:
row.style.color = "powderblue";
}
cell1.innerHTML = new Date().toISOString();
cell2.innerHTML = sender;
cell3.innerHTML = text;
<div class="container">
<div class="left-side">
<table>
<tr>
<td>Enter a text</td>
<td><input id="inputText" type="text" /></td>
</tr>
<tr>
<td>Send as text</td>
<td><input type="submit" value="Send" onclick="sendTextMessage();"/></td>
</tr>
</table>
<table>
<tr>
<td>Enter a number</td>
<td><input id="inputNumber" type="number" /></td>
</tr>
<tr>
<td>Send as number</td>
<td><input type="submit" value="Send" onclick="sendNumberMessage();"/></td>
</tr>
</table>
</div>
<div class="right-side">
<table id="outputTable">
<tr>
<th>Date</th>
<th>Sender</th>
<th>Message</th>
</tr>
</table>
</div>
</div>
</body>
</html>
Code is complete and was tested under Payara 4.1. Example is pure standard (no external library/framework)
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (con != null) {
try {
con.close(); // free all resources
} catch (JMSException e) { /* Ignore */ }
}
}
}
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
try {
con = factory.createConnection();
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); // non-transacted
session
Using MessageListener
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
try {
con = factory.createConnection();
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); // non-transacted
session
Queue queue = session.createQueue("test.queue"); // only specifies queue name
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
try {
if (!(msg instanceof TextMessage))
throw new RuntimeException("no text message");
TextMessage tm = (TextMessage) msg;
System.out.println(tm.getText()); // print message
} catch (JMSException e) {
System.err.println("Error reading message");
}
}
});
con.start(); // start the connection
Thread.sleep(60 * 1000); // receive messages for 60s
} catch (JMSException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
con.close(); // free all resources
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
For using ConnectionFactory client must execute JNDI lookup (or use injection). The following code gets JNDI
InitialContext object and uses it to lookup for ConnectionFactory object under JNDI name:
The methods available in this interface are createConnection() methods that return a Connection object and new
JMS 2.0 createContext() methods that return a JMSContext.
JMSContext createContext();
JMSContext createContext(String userName, String password);
JMSContext createContext(String userName, String password, int sessionMode);
JMSContext createContext(int sessionMode);
}
import java.util.Properties;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
} catch (NamingException e) {
e.printStackTrace();
} catch (JMSException e) {
e.printStackTrace();
} finally {
try {
if (sender != null)
sender.close();
if (session != null)
session.close();
if (conn != null)
conn.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
check this
instead of
msg.setText(message);
GoogleMailTest() {
public static void Send(final String username, final String password, String recipientEmail,
String title, String message) throws AddressException, MessagingException {
GoogleMailTest.Send(username, password, recipientEmail, "", title, message);
}
public static void Send(final String username, final String password, String recipientEmail,
String ccEmail, String title, String message) throws AddressException, MessagingException {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
// Get a Properties object
Properties props = System.getProperties();
props.setProperty("mail.smtps.host", "smtp.gmail.com");
props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.port", "465");
props.put("mail.debug", "true");
props.setProperty("mail.smtp.socketFactory.port", "465");
props.setProperty("mail.smtps.auth", "true");
props.put("mail.smtps.quitwait", "false");
Session session = Session.getInstance(props, null);
// -- Create a new message --
final MimeMessage msg = new MimeMessage(session);
// -- Set the FROM and TO fields --
msg.setFrom(new InternetAddress(username));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail, false));
JOptionPane.showMessageDialog(null, msg.getSize());
if (ccEmail.length() > 0) {
msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(ccEmail, false));
}
msg.setSubject(title);
msg.setText(message);
msg.setSentDate(new Date());
SMTPTransport t = (SMTPTransport) session.getTransport("smtps");
t.connect("smtp.gmail.com", username, password);
t.sendMessage(msg, msg.getAllRecipients());
try {
if (cc.isEmpty()) {
GoogleMailTest.Send(senderMail, password, toMail, title, msg);
} else {
GoogleMailTest.Send(senderMail, password, toMail, cc, title, msg);
}
} catch (MessagingException ex) {
Logger.getLogger(GoogleMailTest.class.getName()).log(Level.SEVERE, null, ex);
}
}