The Magnificent java EE 7 in Wildfly-O-Rama

Post on 10-May-2015

2.024 views 0 download

description

My Hands on for Java EE 7 You can grab the code on github : https://github.com/antoinesd/magnificent-java-ee7

Transcript of The Magnificent java EE 7 in Wildfly-O-Rama

The Magnificent Java EE 7 in Wildfly-O-Rama

Antoine Sabot-Durand Java EE Expert

Senior Software Developer @ Red Hat @antoine_sd

Antoine Sabot-DurandSenior Software Developer at Red Hat

Architect and Tech consultant

16 years in IT

Java & OSS :

CDI co-spec lead

CDI community development

Agorava technical leader

@antoine_sd

What’s in there ?

Short Java EE 7 Intro

Java EE History

Java EE 7 main features

Java EE 7 Content

WildFly

WildFly Roadmap

Java EE 7 in action

Java EE Introduction

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

J2EE 1.3 09/24/2001

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

J2EE 1.3 09/24/2001

J2EE 1.4 11/11/2003

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

J2EE 1.3 09/24/2001

J2EE 1.4 11/11/2003

Java EE 5 05/11/2006

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

J2EE 1.3 09/24/2001

J2EE 1.4 11/11/2003

Java EE 5 05/11/2006

Java EE 6 12/10/2009

2012 2013

Java EE History

1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

Java Professional Edition

Mai 1998

J2EE 1.2 12/12/1999

J2EE 1.3 09/24/2001

J2EE 1.4 11/11/2003

Java EE 5 05/11/2006

Java EE 6 12/10/2009

2012 2013

Java EE 7 06/22/2013

WebSocket client/server endpoints

Batch Applications

JSON Processing

Concurrency Utilities

Simplified JMS API

@Transactional and @TransactionScoped

JAX-RS Client API

Pervasive CDI

More annotated POJOs

Faces Flow

Java EE 7 Main Features

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Java EE 7 JSR

EJB 3.2

Servlet 3.1

CDI Extensions

Bea

n Va

lidat

ion

1.1

Batch 1.0

Web Fragments

JCA 1.7JMS 2.0JPA 2.1

Managed Beans 1.0

Concurrency 1.0Common Annotations 1.1

Interceptors 1.2, JTA 1.2CDI 1.1

JSF 2.2,JSP 2.3,EL 3.0

JAX-RS 2.0, JAX-WS 2.2 JSON 1.0 WebSocket

1.0

Previously named JBoss Application Server

Named change to better differentiate Community from Supported product

Support Java EE 7

Fast, Lightweight, Manageable

Developer Friendly

Open Source

JBoss WildFly

Alpha 1 - May 2013

Alpha 2 - June 2013 (Java EE 7 released on the 22)

Alpha 3 - July 2013

Alpha 4 - August 2013

Beta 1 - October 2013

CR1 - December 2013

Final - Mar/Apr 2014

That’s 8/9 months after EE 7 release (better than 2 years for AS 7)

WildFly Roadmap

Download WildFly today

http://wildfly.org

Let’s get some action !

Welcome to Chat-e-Chat-oChat-e-Chat-o is a Startup which develop a SaaS chat service

We raised fund from VC to create their first release

CTO decided to use Java EE 7 to develop the service

As the main developer, you have the task to implement all the feature asked by marketing

Events and name in this HOL are totally fictive

Situation and use cases are simplified

This is your story...

At the beginningAfter reading some doc. You created the first chat demo

It contains 4 files

pom.xml : Maven configuration file

ChatEndPoint.java : A Java class corresponding to a websocket Endpoint

index.html : Home Page of the Service

websocket.js : a little JS lib to exchange with the server

This 4 files are enough to start the application server with a (very) basic chat service

Git Start

git clone git@github.com:antoinesd/magnificent-java-ee7.git!

!

git checkout beginning

pom.xml 1/2

<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.lab.javaee</groupId> <artifactId>demo-chat</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>demo-chat</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies>

pom.xml 2/2<build> <finalName>${project.name}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>1.0.0.Beta1</version> <configuration> <version>8.0.0.CR1</version> </configuration> </plugin> </plugins></build></project>

index.html<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>WebSocket Chat</title> </head> <body> <h1>Chat!</h1> <div style="text-align: center;"> <form action=""> <table> <tr> <td> Users<br/> <textarea readonly="true" rows="6" cols="20" id="userField"></textarea> </td> <td> Chat Log<br/> <textarea readonly="true" rows="6" cols="50" id="chatlogField"></textarea> </td> </tr> <tr> <td colspan="2"> <input id="textField" name="name" value="Duke" type="text"><br> <input onclick="join();" value="Join" type="button"> <input onclick="send_message();" value="Chat" type="button"> </td> </tr> </table> </form> </div> <div id="output"></div> <script language="javascript" type="text/javascript" src="websocket.js"></script> </body> </html>

websocket.jsvar wsUri = "ws://" + document.location.host + "/demo-chat/websocket"; var websocket = new WebSocket(wsUri); var username; var output = document.getElementById("output"); websocket.onopen = function (evt) {onOpen(evt)}; websocket.onmessage = function (evt) {onMessage(evt)}; websocket.onerror = function (evt) {onError(evt)}; !function join() { username = textField.value; websocket.send(username + " joined"); } !function send_message() {websocket.send(username + ": " + textField.value);} !function onOpen() {writeToScreen("Connected to " + wsUri);} !function onMessage(evt) { console.log("onMessage"); writeToScreen("RECEIVED: " + evt.data); if (evt.data.indexOf("joined") != -1) { userField.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n"; } else { chatlogField.innerHTML += evt.data + "\n"; } } !function onError(evt) {writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);} !function writeToScreen(message) {output.innerHTML += message + "<br>";}

ChatEndPoint.javaimport javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.*; !@ServerEndpoint("/websocket") public class ChatEndpoint { private static final Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>()); @OnOpen public void onOpen(Session peer) { peers.add(peer); } @OnClose public void onClose(Session peer) { peers.remove(peer); } @OnMessage public void message(String message, Session client) throws IOException, EncodeException { for (Session peer : peers) { peer.getAsyncRemote().sendText(message); } }}

Files organization & launching

Files must be organized like this way

To launch the app

In your shell, go to the directory containing the pom.xml file

and type :

mvn clean package wildfly:run!

To test the app browse to :

http://localhost:8080/demo-chat

Step 1 : Create a Chat ServiceAs we will add functionalities to our application, we want to separate future business logic from the Websocket endpoint

That’s why we decide to create a ChatService classes to deal with chat business logic

To implement this you’ll have to :

Activate CDI

Create ChatService CDI Bean

Remove all business logic from ChatEndpoint to put it in ChatService

Git Shortcut

git checkout step_1

beans.xml!

Activate CDI

To activate CDI you only have to create an empty file named beans.xml in folder src/main/webapp/WEB-INF

ChatService.java

@ApplicationScopedpublic class ChatService { private final Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>()); public boolean add(Session session) { return peers.add(session); } public boolean remove(Object o) { return peers.remove(o); } public void processMessage(String message) { for (Session peer : peers) { peer.getAsyncRemote().sendText(message); } }}

ChatEndPoint.java

@ServerEndpoint("/websocket") public class ChatEndpoint { @Inject private ChatService service; @OnOpen public void onOpen(Session peer) { service.add(peer); } @OnClose public void onClose(Session peer) { service.remove(peer); } @OnMessage public void message(String message, Session client) throws IOException, EncodeException { service.processMessage(message); }}

Step 2 : Keyword detectionOur main partner is the Poodle website.They want to embed our service but they need a solution to detect keywords in chat to feed their advertising platform

You decide to build a prototype of this feature with CDI built-in observer pattern

To implement this you’ll have to :

Modify ChatService class by :

Injecting an Event generator in the endpoint

Modifying the message() method to fire the event

Create an observer bean for the event

Git Shortcut

git checkout step_2

ChatService.javaimport javax.enterprise.event.Event; import javax.inject.Inject; ...!@ApplicationScopedpublic class ChatService { ... @Inject private Event<String> events; ... public void processMessage(String message) { if (message.toLowerCase().indexOf("world") > -1) events.fire(message); for (Session peer : peers) { peer.getAsyncRemote().sendText(message); } } }

Bean with Observer

import javax.enterprise.event.Observes; public class MessageObserver { public void observesWorldMessages(@Observes String msg) { System.out.println("Keyword was trapped : " + msg); } }

Keyword detection resultIn your shell, go to the directory containing the pom.xml file

and type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

When you type a message with «world» inside an event is fired and an alert is written on the console

Step 3 : More decoupling with AOPOk, our system to detect certain keywords works nicely with some kind of decoupling thanks to CDI events

But the team is not happy to have this code directly into the service. If other filtering needs occur, we’ll have a big if cascade in processMessage() method

So you decide to use AOP and interceptor to externalize event firing from your main code :

Extract interface from ChatService to allow use of Decorator

Create a decorator to track Ad Word in the ChatService processMessage() method

Move the event generator to the Decorator

Git Shortcut

git checkout step_3

Extract Interface from ChatService

public interface ChatService { boolean add(Session session); boolean remove(Object o); void processMessage(String message);}

@ApplicationScopedpublic class ChatServiceImpl implements ChatService {!… !}

Create the PoodleAdWord Decorator@Decorator@Priority(Interceptor.Priority.APPLICATION) public abstract class PoddleAddWordDecorator implements ChatService { @Inject @Delegate private ChatService delegateService; private final List<String> adWords = new ArrayList<String>() {{ add("world"); add("duck"); add("cartman"); }}; @Inject private Event<String> events; // This should be moved from ChatServiceImpl class @Override public void processMessage(String message) { String lmessage = message.toLowerCase(); for (String s : adWords) { if (lmessage.indexOf(s) > -1) { events.fire(s); } } delegateService.processMessage(message); }}

Final version of ChatServiceImpl

@ApplicationScopedpublic class ChatServiceImpl implements ChatService { private final Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>()); @Override public boolean add(Session session) { return peers.add(session); } @Override public boolean remove(Object o) { return peers.remove(o); } @Override public void processMessage(String message) { for (Session peer : peers) { peer.getAsyncRemote().sendText(message); } }}

More decoupling resultOur business code doesn’t contain cross cuting concern anymore

We’re ready to add other filters with this pattern

To test, type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

When you type a message containing an ad word (i.e. duck) an event is fired and an alert is written on the console

Step 4 : Please be politeThe famous Mapple company is also very interested in our chat platform

But they have a strict policy regarding bad words. So they need a solution to catch bad words, get notification and replace them by good ones.

As Poodle pay us more we want to give priority to their ad filter to be sure that «bad» keywords are still trapped for them

You know how to build this filter

Create Qualifiers to differentiate Ad Word events from Bad Word events

Build a new decorator to test messages content and correct impolite words

Configure decorator priority to have the Poodle one in first

Change existing event generator to add qualifiers top them

Git Shortcut

git checkout step_4

Create the AdWord qualifier

import javax.inject.Qualifier; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME;!@Qualifier@Documented@Retention(RUNTIME) public @interface AdWord { }

Create the BadWord qualifier

import javax.inject.Qualifier; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME;!@Qualifier@Documented@Retention(RUNTIME) public @interface BadWord { }

Create the Mapple Decorator@Decorator@Priority(Interceptor.Priority.APPLICATION + 10) public abstract class MapplePoliteDecorator implements ChatService { static final Map<String, String> DICTIONARY = new HashMap<String, String>() {{ put("fuck", "duck"); put("crap", "trap"); put("idiots", "world"); put("cartman", "Stan"); }}; @Inject @Delegate private ChatService delegateService; @Inject @BadWord private Event<String> events; @Override public void processMessage(String message) { String lmessage = message.toLowerCase(); String res = message; for (String word : DICTIONARY.keySet()) if (lmessage.indexOf(word) > -1) { res = res.replaceAll("(?i)" + word, DICTIONARY.get(word)); events.fire(word); } delegateService.processMessage(res); }}

Change existing code to introduce AdWord qualifier

@Decorator@Priority(Interceptor.Priority.APPLICATION) public abstract class PoddleAddWordDecorator implements ChatService {… @Inject @AdWord private Event<String> events; … }

public class MessageObserver { public void observesAdWords(@Observes @AdWord String word) { System.out.println("Ad word trapped : " + word); } public void observesbadWords(@Observes @BadWord String word) { System.out.println("Bad word trapped : " + word); }}

Be polite resultWe created a new Decorator that change message content after the 1st decorator did its job.

We’ll feed those dictionaries with a database later

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

When you type a message containing a bad word inside, message is changed, an event is fired and an alert is written on the console.

If the bad word is also an ad word. The Addword is still track thanks to priority

Step 5 : Intercept to logOur code is nice with all this Decorators, but we’d like to have an easy way to trace what code is called without modifying it

For that we’ll need another kind of AOP : an interceptor to log information

Create an interceptor binding

Create the Log interceptor

Use it in our code

Git Shortcut

git checkout step_5

Create the Interceptor Binding

import javax.interceptor.InterceptorBinding; import java.lang.annotation.ElementType;import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target; @InterceptorBinding@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME) public @interface Log { }

Create the interceptor

@Interceptor@Log@Priority(Interceptor.Priority.APPLICATION) public class LoggerInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { System.out.println("*** Before " + ctx.getMethod().toString()); Object res = ctx.proceed(); System.out.println("*** After " + ctx.getMethod().toString()); return res; }}

Using the Interceptor

@ApplicationScoped@Logpublic class ChatServiceImpl implements ChatService {… }

Log Interceptor resultWe created an interceptor and its binding to activate logging by annotation

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

All calls on ChatServiceImpl will be logged to the console

Step 6 : Plastic surgeryLet’s face the cruel truth our UI is totally ugly!

As we decided to use Java EE stack, we give a try to JSF for our front

Out of the box JSF doesn’t provide rich component, we’re going to use Primefaces to produce a nice UI proposal

So, in this step, we’ll develop a JSF chat page to enhance the user experience

Git Shortcut

git checkout step_6

Adding PrimeFaces to the POM

<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> … <dependencies> … <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>4.0</version> <scope>runtime</scope> </dependency> </dependencies> … </project>

The JSF Page<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>WebSocket Chat</title> </h:head> <h:body> <p:layout style="width:1024px;height:600px;margin-left: auto;margin-right: auto;"> <p:layoutUnit position="center" header="Chat-e-Chat-o"> <div style="text-align: center;"> <h:form id="chat"> <table> <tr> <td> Users<br/> <p:inputTextarea readonly="true" rows="6" cols="20" id="userField"/> </td> <td> Chat Log<br/> <p:inputTextarea readonly="true" rows="6" cols="50" id="chatlogField"/> </td> </tr> <tr> <td colspan="2"> <p:inputText id="textField" name="name" value="Duke"/><br/> <p:commandButton onclick="join();" value="Join" id="join"/> <p:commandButton onclick="send_message();" value="Chat" id="chat"/> </td> </tr> </table> </h:form> </div> <div id="output"></div> <script language="javascript" type="text/javascript" src="websocket.js"></script> </p:layoutUnit> </p:layout> </h:body> </html>

var wsUri = "ws://" + document.location.host + "/demo-chat/websocket"; !... !var userField = document.getElementById("chat:userField"); var chatlogField = document.getElementById("chat:chatlogField"); var textField = document.getElementById("chat:textField"); !... function join() { username = textField.value; websocket.send(username + " joined"); document.getElementById("chat:join").disabled = true; }

Modification of websocket.js

JSF generates different component id so we have to adapt the code

We also choose to disable the «join» button after usage to give focus to chat button

New content for index.html

<html> <head> <meta http-equiv="Refresh" content="0; URL=index.jsf"> </head> </html>

Plastic surgery resultWe got a better UI and user experience. Ok there’s still work to do, but we have the POC here ;)

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

Beautiful isn’t it ?

Step 7 : A bit of structureSo we get message and dispatch them to all people, but we don’t do anything of them

It could be nice to receive structured message in JSON format and create our own object from it

In this step we’re gone :

Change client side js to generate JSON structure with username and message

Create a Message class to contain the java version of this JSON structure

Change the ChatService bean to deserialize JSON message with the new JSONP specification

Git Shortcut

git checkout step_7

Websocket.js modification

function send_message() { var msg = new Object(); msg.user = username; msg.content = textField.value; websocket.send(JSON.stringify(msg)); } ...function onMessage(evt) { console.log("onMessage : " + evt.data); writeToScreen("RECEIVED: " + evt.data); if (evt.data.indexOf("joined") != -1) { userField.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n"; } else { var msg = JSON.parse(evt.data) chatlogField.innerHTML += msg.content + " said " + msg.user + "\n"; }

New Message Class

public class Message { private String user; private String content; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }

ChatService processMessage modification

public void processMessage(String message) { System.out.println(message); JsonReader reader = Json.createReader(new StringReader(message)); try { JsonObject msgObj = reader.readObject(); Message msg = new Message(); msg.setUser(msgObj.getString("user")); msg.setContent(msgObj.getString("content")); System.out.println("Message from " + msg.getUser() + " : " + msg.getContent()); } catch (JsonParsingException e) { System.out.println("Message is not in JSON format"); } finally { reader.close(); } for (Session peer : peers) { peer.getAsyncRemote().sendText(message); }}

Structure resultWe got now a data structure for all the messages. We can use it to provide other service (history, search, etc...)

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

Nothing changed... But look at the console...

Step 8 : Save our messagesNow we have messages in a nice Pojo. What about persist them to provide new services

In this step we’re gone :

Add a JPA configuration

Turn our Pojo into a JPA entity

Create a service to handle message

Git Shortcut

git checkout step_8

Persistence.xml in META-INF

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="chatPU" transaction-type="JTA"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <!-- Properties for Hibernate --> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> </properties> </persistence-unit> </persistence>

public class ConfigPersistence { @PersistenceContext @Produces private EntityManager em; }

ConfigPersistence.java

@Entity@Vetoedpublic class Message { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "USERNAME") private String user; private String content; ... ! public Long getId() { return id; } public void setId(Long id) { this.id = id; } }

Message.java modification

We transform the class in an entity.

Note the @Vetoed CDI annotation that exclude the class from being a bean

We should also change the column name of user. Because user is a reserved name in SQL

ChatService modificationpublic class ChatServiceImpl implements ChatService {… @Inject EntityManager em; … @Override public void persistMessage(Message msg) { em.persist(msg); }… @Override @Transactional public void processMessage(String message) { System.out.println(message); JsonReader reader = Json.createReader(new StringReader(message)); try { JsonObject msgObj = reader.readObject(); Message msg = new Message(); msg.setUser(msgObj.getString("user")); msg.setContent(msgObj.getString("content")); System.out.println("Message from " + msg.getUser() + " : " + msg.getContent()); persistMessage(msg); } catch (JsonParsingException e) { System.out.println("Message is not in JSON format"); } finally { reader.close(); } for (Session peer : peers) { peer.getAsyncRemote().sendText(message); } }}

Save our messages resultWe saved our messages to provide future services (history, search, etc...)

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

Nothing changed... But look at the console...

Step 9 : Message in a bottlePoodle plan to provide an appliance to send ad to our application

This appliance uses JMS to receive information asynchronously

So we decide to build a POC to test Messaging feature in Java EE 7

Create a message queue and a Sender Service

Modify PoodleAdWord Decorator to use the sender Bean

Create a MDB to mock appliance side and listen to our messages

Git Shortcut

git checkout step_9

Message queue and SenderServiceimport javax.annotation.Resource; import javax.inject.Inject; import javax.jms.JMSContext;import javax.jms.JMSDestinationDefinition; import javax.jms.Queue;@JMSDestinationDefinition(name = "java:global/jms/myQueue", resourceAdapter = "jmsra", interfaceName = "javax.jms.Queue", destinationName = "classicQueue", description = "My Sync Queue") public class JmsSenderService { @Resource(mappedName = "java:global/jms/myQueue") private Queue myQueue; @Inject private JMSContext jmsContext; public void sendMessage(String message) { jmsContext.createProducer().send(myQueue, message); }}

PoodleAdWord decorator modification

public abstract class PoddleAddWordDecorator implements ChatService { … @Inject JmsSenderService jms; … @Override public void processMessage(String message) { String lmessage = message.toLowerCase(); for (String s : adWords) { if (lmessage.indexOf(s) > -1) { jms.sendMessage(s); } } delegateService.processMessage(message); }}

The Message Driven Beanimport org.lab.javaee.chat.AdWord; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.enterprise.event.Event;import javax.inject.Inject; import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "java:global/jms/myQueue"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), }) public class PoodleAdListener implements MessageListener { @Inject @AdWord private Event<String> adWordEvents; @Override public void onMessage(Message msg) { try { System.out.println("**** Poodle MDB receiving Ad Word thru jms : " + msg.getJMSMessageID()); adWordEvents.fire(msg.getBody(String.class)); } catch (JMSException e) { throw new RuntimeException("Something nasty happened", e); } }}

Message in a bottle resultWe put a messaging system in place with two classes and two annotations

To test type : mvn clean package wildfly:run!

To test the app browse to : http://localhost:8080/demo-chat

Enter an ad word and check the console

Questions