Accueil > Intranet Michel Buffa > JavaEE6 Weblogic exercice 1

JavaEE6 Weblogic exercice 1

De $1

Table des matières
  1. 1. Introduction
  2. 2. Install the different components
  3. 3. Set Weblogic to deploy "exploded archives"
  4. 4. Creating a "web profile JavaEE 6 project", write a servlet and an EJB for testing
    1. 4.1. Add a servlet, run the project
    2. 4.2. Add a stateless session bean EJB
    3. 4.3. Call the session bean from the servlet
  5. 5. Add JPA to the project, access a javaDB database
    1. 5.1. Check that Derby/JavaDB is running, connect the sample database to Eclipse and to Weblogic
    2. 5.2. Add JPA facet to the project
    3. 5.3. Configure the persistence.xml file using the design tool
    4. 5.4. Add a JPA entity class mapped to the Manufacturer table of the sample database
    5. 5.5. Write a stateless session bean facade for this entity class
    6. 5.6. Let's the servlet use the session bean facade
  6. 6. Use a JSP with JSTL and EL to display the results
    1. 6.1. Modify the servlet so that the request if forwarded to a JSP for displaying the data
    2. 6.2. Add a JSP page for viewing the data, using JSTL and EL
  7. 7.  Implements the rest of the CRUD operations
    1. 7.1. Add several methods to the ManufacturerFacade.java file
    2. 7.2. And modify the Servlet so that it can do several actions depending on HTTP parameters
  8. 8. Adapt the JSP so that you can remove, add or search a manufacturer
  9. 9. Play with CDI, add injectable beans to the project, use of CDI qualifiers
    1. 9.1. Enable CDI in your project
    2. 9.2. Create an injectableBeans package, add a TextDecorator interface
    3. 9.3. Add a beans that implements the TextDecorator interface, names BoldMaker
    4. 9.4. Inject the bean in the Servlet, test the project
    5. 9.5. Add a second injectable bean for decorating Strings in italic
    6. 9.6. Create two qualifier annotations, qualify the beans, qualify the injection
  10. 10. Using interceptors
    1. 10.1. Add an interceptor binding type annotation to the project named @Logging
    2. 10.2. Add an interceptor that implements the @Logging annotation/interface
    3. 10.3. Update the beans.xml
    4. 10.4. Annotated the ManufacturerFacade for logging, run the project

  1. 1. Introduction
  2. 2. Install the different components
  3. 3. Set Weblogic to deploy "exploded archives"
  4. 4. Creating a "web profile JavaEE 6 project", write a servlet and an EJB for testing
    1. 4.1. Add a servlet, run the project
    2. 4.2. Add a stateless session bean EJB
    3. 4.3. Call the session bean from the servlet
  5. 5. Add JPA to the project, access a javaDB database
    1. 5.1. Check that Derby/JavaDB is running, connect the sample database to Eclipse and to Weblogic
    2. 5.2. Add JPA facet to the project
    3. 5.3. Configure the persistence.xml file using the design tool
    4. 5.4. Add a JPA entity class mapped to the Manufacturer table of the sample database
    5. 5.5. Write a stateless session bean facade for this entity class
    6. 5.6. Let's the servlet use the session bean facade
  6. 6. Use a JSP with JSTL and EL to display the results
    1. 6.1. Modify the servlet so that the request if forwarded to a JSP for displaying the data
    2. 6.2. Add a JSP page for viewing the data, using JSTL and EL
  7. 7.  Implements the rest of the CRUD operations
    1. 7.1. Add several methods to the ManufacturerFacade.java file
    2. 7.2. And modify the Servlet so that it can do several actions depending on HTTP parameters
  8. 8. Adapt the JSP so that you can remove, add or search a manufacturer
  9. 9. Play with CDI, add injectable beans to the project, use of CDI qualifiers
    1. 9.1. Enable CDI in your project
    2. 9.2. Create an injectableBeans package, add a TextDecorator interface
    3. 9.3. Add a beans that implements the TextDecorator interface, names BoldMaker
    4. 9.4. Inject the bean in the Servlet, test the project
    5. 9.5. Add a second injectable bean for decorating Strings in italic
    6. 9.6. Create two qualifier annotations, qualify the beans, qualify the injection
  10. 10. Using interceptors
    1. 10.1. Add an interceptor binding type annotation to the project named @Logging
    2. 10.2. Add an interceptor that implements the @Logging annotation/interface
    3. 10.3. Update the beans.xml
    4. 10.4. Annotated the ManufacturerFacade for logging, run the project

Introduction

In this exercice we will write a JavaEE application that uses the new "web profile": a single project that can use JSF/JSP/Servlets/Web Services/EJBs/JPA with some limitations (no message driven beans, no timer, etc.) and one advantage: it is easier to manage as all components are located in a single project, and deployment is done in a single jar file. We will use a javaDB/Derby database for this application.

Install the different components

  1. You will need to install weblogic 12c with the Oracle Enterprise Pack for Eclipse (an Eclipse 3.7 for java EE developers that is pre configured with the Weblogic server plugin and some other goodies). Follow the directives of your instructor. Do not forget to create a default domain in your server. When asked for a password, please enter one you will not forget !
  2. Install the JavaDB/Derby database. Follow the instructions from this page (installing a java DB database): Weblogic / Java EE6 FAQ and Guides.
  3. In some other exercices we will use a MySQL database. You may want to install it now also. Again, look at the proper section in:  Weblogic / Java EE6 FAQ and Guides.

Set Weblogic to deploy "exploded archives"

Du to a weblogic 12c bug, project with the "web profile" raise some unexpected errors in the persistence context when the project properties are not set to "deploy as exploded archives" (see )

In the Java EE perspective, locate the server tab, right click on the weblogic server and choose "properties".

In the dialog, go to weblogic/publishing and click on "publish as exploded archives".

 

Creating a "web profile JavaEE 6 project", write a servlet and an EJB for testing

Add a servlet, run the project

Create a standard dynamic web project using Eclipse, call it for example "Exo1WebProfile". Click twice on the next button and in the last screen, check "Generate web.xml descriptor". Normally we do not need any XML settings in that file (JavaEE 6 replaced the need to XML settings by code annotations) but it will be useful to set the starting URL when we run the project in that file.

Add a Servlet in the project, call it TestServlet, and add it in a package named "servlets". Click finish. Add some lines in the doGet() method, like these:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Set the response message's MIME type.
response.setContentType("text/html;charset=UTF-8");
// Allocate a output writer to write the response message into the network socket.
PrintWriter out = response.getWriter();

out.println("<h1>Welcome to Exo1 test servlet!</h1>");
}

 Then modify the web.xml file in order to start the project directly with this servlet. If you look at the Servlet code you will find an annotation that set the URL pattern for the servlet: @WebServlet("/TestServlet"), so let's modifiy the <welcome-file> entry in the web.xml descriptor:

 <welcome-file-list>
<welcome-file>TestServlet</welcome-file>
</welcome-file-list>

Then right click the project, do Run As/Run on server. The first time you will be asked to choose a server. Select "weblogic 12c", check the checkbox so that thos will be a default value for this project and click finish. This should launch weblogic, the console tab should appear, etc. And after a while you should see a browser inside Eclipse showing "Welcome to Exo1 test servlet!".

Add a stateless session bean EJB

Now we will add a stateless session bean EJB to the project. Select as the type of element to add to the project "session bean (EJB 3.x)", add it in a package named "sessions" and name it "HelloWorldBean". Notice that in the wizard you have a select menu for stateless/stateful/singleton and below you may choose to implement some local or remote interfaces. For the moment leave everything with default values (stateless, no interface). Click finish.

Add a method to this EJB. The code should look like this:

package sessions;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

/**
* Session Bean implementation class HelloWorldBean
*/
@Stateless
@LocalBean
public class HelloWorldBean {

/**
* Default constructor.
*/
public HelloWorldBean() {
// TODO Auto-generated constructor stub
}

public String helloWorld() {
return "hello world!";
}
}

Notice the @Stateless and @LocalBean that indicates that we have a stateless session bean that can be only called from the same JVM. Remote session beans are not allowed in a web profile project.

Call the session bean from the servlet

In order to call the bean from the servlet, we need to add a few lines of code. 

Inject the EJB reference into the servlet: add these line below the class declaration:

	@EJB
HelloWorldBean bean;

 Fix the imports then add these lines in the doGet() method:

  out.println("The call to the HelloWorld EJB returned: " + bean.helloWorld());

Then run the project. If everything was ok, you should see a message : "The call to the HelloWorld EJB returned: hello world!".

The @EJB annotation has injected a reference to an instance of the HelloWorld bean. You do not now if a "new" has been performed or if an existing instance has been used or if there are more than one instance running in the web server.

Add JPA to the project, access a javaDB database

Check that Derby/JavaDB is running, connect the sample database to Eclipse and to Weblogic

Normally you should have installed a Derby/JavaDB database to your system using informations from:  Weblogic / Java EE6 FAQ and Guides. Check that you added the database both in Eclipse and in Weblogic.

If you followed the instructions in Eclipse, in the java EE perspective, under the "Data Source Explorer" tab you should see the "sample" javaDB database, and be able to consult the data in the different tables:

javaDBInEclipse.jpg 

And the data:

javaDBData.jpg

Add JPA facet to the project

Eclipse may help you a lot working with JPA but you need to add a JPA facet to the project. Right click on the project then properties/project facets and check the JPA facet. Validate.
 

jpa facet.jpg

Look at your project, a persistence.xml file has been added under META-INF but there is also a new node in your project called "JPAcContent" that makes persistence.xml easier to access.

persistence.xml.jpg

This file is used for configuring the different persistence units that you will use in your project. A persistence unit is an object you will use from your code that works with a given database, using an Object Relational Mapping tool like Hibernate, Eclipse Link, Toplink, etc. This file also is useful for specifying if you are allowed to create tables, alter or delete tables, etc. 

Current persistence.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="Exo1WebProfile" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<properties>
<property name="eclipselink.target-server" value="WebLogic" />
</properties>
</persistence-unit>
</persistence>

 As you may see it says that we are going to work woth eclipseLink (provider node...), that we are going to work with a local database (transaction-type), etc... these values are not sufficient for what we want to do.

Configure the persistence.xml file using the design tool

Double click on the persistence.xml file, it shoud bring a designer. We are going to configure the database connection, so click on the connection tab at the bottom of the designer. We are using a remote database through a connection pool open on the we logic server. We will just need the JNDI name of the database. 

Change the Transaction type from "RESOURCE LOCAL" to "JTA" in the wizard, and enter the JNDI name of the sample database as it has been configured in WebLogic. If you followed the instructions from:  Weblogic / Java EE6 FAQ and Guides it should be "sample".  Ok, you are done with persistence.xml for the moment...

Add a JPA entity class mapped to the Manufacturer table of the sample database

Let's add an entity class generated from the Manufacturer table of the sample database... add a "jpa entity from table" to the project. If asked to what project you want to add the entity, select the current project. Click next.

Then you are asked to choose the connection and table. Select the connection you added to Eclipse + select APP as a schema. The table will appear, then check MANUFACTURER

Click twice next. Enter "entities" as the package name.

On the last screen you may change the name of the class but it is better to leave the proposed name ("Manufacturer") unchanged.

Click finished. Look at your project, under the "entities" package you should have a Manufacturer.java file that looks like this:

@Entity
public class Manufacturer implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@Column(name="MANUFACTURER_ID")
private int manufacturerId;

private String addressline1;

private String addressline2;

private String city;

private String email;

private String fax;

private String name;

private String phone;

private String rep;

private String state;

private String zip;

public Manufacturer() {
} ... // Getters and Setters... }

The @Entity annotation says "I'm mapped to a table whose name of my name capitalized" (in that case, the MANUFACTURER table).

The other annotation @Id says that the field is a primary key, and the @Column indicated the name of the column because default rules could not be applied here (defaut = name of the field in upper cases).

Write a stateless session bean facade for this entity class

Usually, one best practice consists in associating a "facade" with each entity class. A facade is a staless or stafull session bean that will provide CRUD operations on the associated model/entity, as well as convenience functions. Some IDEs like Netbeans or IDEA propose wizards that automate this process. Oracle Enterprise Pack for Eclipse proposes also such a wizard but only for oracle ADF projects ("Application Development Framework"). These are projects that use JSF and some special faces libraries from Oracle. They will not be studied during this training.

So let's add a stateless session facade for Manufacturers to this project. Add a new stateless session bean, put it in the "sessions" package, name it ManufacturerFacade.

Add in that bean an EntityManager so that we will be able to "talk" to the database and do other interesting things. Just add these two lines as class attributes:

    @PersistenceContext
EntityManager em;

Add also a method that gets all the existing manufacturers from the table (the JPQL request is equivalent to a SQL select *...). Your session facade should look like that:

package sessions;

import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import entities.Manufacturer;

@Stateless
@LocalBean
public class ManufacturerFacade { @PersistenceContext
EntityManager em;

public List<Manufacturer> getManufacturers() {
return (List<Manufacturer>)em.createQuery("select m from Manufacturer m").getResultList();
}
}

 

Notice that executing this request does not return tuples but a Collection of Objects (in our case a List of instances of Manufacturer).

Let's the servlet use the session bean facade

In order to use our new EJB from the Servlet, simply inect it using the @EJB annotation, exactly like we did with the HelloWorldBean. Let's ass this on the Servlet, as well as a call to the getManufacturers() method. We will just display the resulting list by generating some HTML in the response:

Here is the new code of the Servlet:

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import entities.Manufacturer;
import sessions.HelloWorldBean;
import sessions.ManufacturerFacade;


@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

@EJB
HelloWorldBean bean;
@EJB
ManufacturerFacade mf;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Set the response message's MIME type.
response.setContentType("text/html;charset=UTF-8");
// Allocate a output writer to write the response message into the network socket.
PrintWriter out = response.getWriter();

out.println("<h1>Welcome to Exo1 test servlet!</h1>");

out.println("The call to the HelloWorld EJB returned: " + bean.helloWorld());

out.println("<h1>List of manufacturers in the sample database</h1>");
// Use session facade to get all manufacturers
Collection<Manufacturer> manufacturers = mf.getManufacturers();
// Iterate the Collection and diplay manufacturers ids + names in a table
out.println("<TABLE border='2' frame='box'>");
for (Manufacturer m : manufacturers) {
out.println("<TR>");

out.println("<TD>" + m.getManufacturerId() + "</TD><TD>" + m.getName() +"</TD>");

out.println("</TR>");
              }
out.println("</TABLE>");

}
}

 Run the project, it should display the list of all the manufacturers from the MANUFACTURER table of the sample database.

Use a JSP with JSTL and EL to display the results

If you followed the training about Java EE Web Components or if you have some skills you should know that it is not a good practice to print HTML code in a servlet. We are going to use a JSP file for viewing the list of manufacturers while the servlet will act as a pure HTTP controller. This is called model 2 architecture (a form of MVC tailored for HTTP applications, see http://en.wikipedia.org/wiki/Model_2) . Servlet is a controller, JSP a view and EJBs are dedicated for business processing and data access.

Modify the servlet so that the request if forwarded to a JSP for displaying the data

Modify the servlet so that the HTTP request is passed to a JSP for finishing the treatment (generating the view). The trick consists in a adding to the request the data to be displayed before forwarding the request to the JSP:

New version of the servlet :

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// delegate business to the session bean facade
Collection<Manufacturer> manufacturers = mf.getManufacturers();
// Put data to display on the request
request.setAttribute("manufacturers", manufacturers);

// Forward the request to the view. Servlet and JSP are parts of same request
RequestDispatcher dispatcher = request.getRequestDispatcher("DisplayManufacturers.jsp");
dispatcher.forward( request, response );
}

Add a JSP page for viewing the data, using JSTL and EL

Add a JSP file in your project, name it "DisplayManufacturers.jsp" and replace the generated content by this one:

<%@ 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>

<html>
<head>
<title>List of Manufacturers</title>
</head>
<body>


<table border="2" frame="box">
<tr>
<th>Id</th>
<th>Name</th>
<th>Phone</th>
<th>Email</th>
</tr>

<c:forEach var="m" items="${manufacturers}">
<tr>
<td>${m.manufacturerId}</td>
<td>${m.name}</td>
<td>${m.phone}</td>
<td>${m.email}</td>
</tr>
</c:forEach>
</table>

</body>
</html>

Notice the line:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix ="c" %>

That indicates that we are going to use the JSP Standard Tag Library (JSTL), all elements with the namespace c: will use the core part of the JSTL (if, then, else, forEach, etc). In order to display the manufacturers' properties we use the EL expression langage. For example, ${m.name} will call the getName() accessor of the bean Manufacturer.java, if there is no such method, we would get "EL property not found" errors.

The line: 

<c:forEach var="m" items="${manufacturers}">

Defines a loop on a collection named "manufacturers". This collection will be searched in different scopes: the page, the request, the session, the context, etc. As in the servlet we put an attribute named "manufacturers" in the request, it is the one that will be found. 

More informations about JSTL and EL are available here:

 Implements the rest of the CRUD operations

We will now add some methods to the facade in order to be able to create new manufacturers, remove an existing manufacturer or look for a manufacturer...

Add several methods to the ManufacturerFacade.java file

In that example we added some "classic" methodes for a facade. Look at them as we will modify the HTTP part of the application later on (Servlet, JSP) in order to exploit these new functionnalities...

package sessions;

import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import entities.Manufacturer;

@Stateless
@LocalBean
public class ManufacturerFacade {
@PersistenceContext
EntityManager em;

public List<Manufacturer> getManufacturers() {
return (List<Manufacturer>)em.createQuery("select m from Manufacturer m").getResultList();
}
// Do an insert in the database
public Manufacturer create(int id, String name, String phone, String email) {
Manufacturer m = new Manufacturer();
m.setManufacturerId(id);
m.setName(name);
m.setPhone(phone);
m.setEmail(email);
// insert in database
em.persist(m);
return m;
}

// The select by id is done using the em.find(class, id) method. Do not use a select where id=:id...
public Manufacturer findById(int id) {
return em.find(Manufacturer.class, id);
}

// Do a select where name=... as more than one manufacturer may have the same name,
// we return here a Collection...
public List<Manufacturer> findByName(String name) {
Query q = em.createQuery("select m from Manufacturer m where m.name = :name");
q.setParameter("name", name);

return (List<Manufacturer>) q.getResultList();
}

// Select manufacturers using a star and end index, useful for pagination
// Get Manufacturers 1-10, then 11-20, etc...
public List<Manufacturer> findRange(int[] range) {
Query q = em.createQuery("select m from Manufacturer m");
q.setMaxResults(range[1] - range[0]);
q.setFirstResult(range[0]);
return (List<Manufacturer>) q.getResultList();
}

// Returns the number of manufacturers
public long count() {
Query q = em.createQuery("select count(m) from Manufacturer m");
long count = ((Long) q.getSingleResult()).longValue();
return count;
}
// remove an object that has the same id. We must first locate an objet with this id
// as the em.remove expects a managed object
public void remove(int id) {
em.remove(findById(id));
}

// Will delete an object that has the same id as m.id
public void remove(Manufacturer m) {
// m has to be managed before being removed. em.merge(m) does not manage m but returns
// a managed object instead.
em.remove(em.merge(m));
}

// Will do an update in the database
public void update(Manufacturer m) {
em.merge(m);
}
}

And modify the Servlet so that it can do several actions depending on HTTP parameters

We modified the Servlet a little so that depending on a HTTP parameter called "action", the servlet can do different operations. In that example we only added "add" and "remove" operations. Look at the code and try to understand the logic of the operations...

Copy and paste this code, run the project.

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import entities.Manufacturer;
import sessions.HelloWorldBean;
import sessions.ManufacturerFacade;


@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

@EJB
HelloWorldBean bean;
@EJB
ManufacturerFacade mf;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String action = request.getParameter("action");
if(action != null) {
if(action.equals("list")) {
displayManufacturers(request, response);
} else if(action.equals("add")) {
int id = Integer.parseInt(request.getParameter("id"));
String name = request.getParameter("name");
String phone = request.getParameter("phone");
String email = request.getParameter("email");

mf.create(id, name, phone, email);

// Then display the updated list
displayManufacturers(request, response);
} else if(action.equals("remove")) {
int id = Integer.parseInt(request.getParameter("id"));
mf.remove(id);
// Then display the updated list
displayManufacturers(request, response);
}

} else {
displayManufacturers(request, response);
}
}

private void displayManufacturers(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// delegate business to the session bean facade
Collection<Manufacturer> manufacturers = mf.getManufacturers();
// Put data to display on the request
request.setAttribute("manufacturers", manufacturers); // number of manufacturers in the database
request.setAttribute("count", mf.count());
// Forward the request to the view. Servlet and JSP are parts of same request
RequestDispatcher dispatcher = request.getRequestDispatcher("DisplayManufacturers.jsp");
dispatcher.forward( request, response );
}
}

Then try this URL in your browser:

http://localhost:7001/Exo1WebProfile/TestServlet?action=add&id=4&name=Buffa&phone=0662454545&email=buffa@unice.fr

This will call the servlet with an action parameter equals to "add", indicating that we want to add a new manufacturer to the database. The rest of the parameters describe the manufacturer we want to add. Normally, this line should add one new Manufacturer and dipslay an updated list. 

Check using the Data Source Explorer tab that the data has been added in the table.

Try also to remove the entry you just inserted:

http://localhost:7001/Exo1WebProfile/TestServlet?action=remove&id=4

Here again, check in the database that the entry has really been removed.

If you try to remove other manufacturers using their IDs, you may encounter some exceptions as some of the manufacturers primary keys have a relationships to some foreign keys in other tables. 

Typical error: "Internal Exception: java.sql.SQLIntegrityConstraintViolationException: DELETE sur la table 'MANUFACTURER' a entraîné la violation de la contrainte de clé externe 'FOREIGNKEY_MANUFACTURER_ID' pour la clé (19985678). L'instruction a été annulée. Error Code: -1"..

Adapt the JSP so that you can remove, add or search a manufacturer

Let's add a form at the beginning of the page, and a link for removing a manufacturer directly in the table. The add and remove and count have been implemented in the servlet but the Search by Name or Remove by name did not have been implemented. You may try this code, then complete the implementation.

<%@ 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>

<html>
<head>
<title>List of Manufacturers</title>
</head>
<body>

<h3>Add a new manufacturer</h3>
<form method="get" action="TestServlet">
Id:<input type="text" name="id" /><br />
Name: <input type="text" name="name" /><br />
Phone: <input type="text" name="phone" /><br />
Email: <input type="text" name="email" /><br />
<input type="hidden" name="action" value="add" />
<input type="submit" value="Add" />
</form>

<h3>Search by name a manufacturer</h3>
<form method="get" action="TestServlet">
Name: <input type="text" name="name" /><br />
<input type="hidden" name="action" value="searchByName" />
<input type="submit" value="Add" />
</form>

<h3>Delete by name a manufacturer</h3>
<form method="get" action="TestServlet">
Name: <input type="text" name="name" /><br />
<input type="hidden" name="action" value="deleteByName" />
<input type="submit" value="Add" />
</form>

<h3>List of manufacturers. There are ${count} manufacturers in the database.</h3>
<table border="2" frame="box">
<tr>
<th>Id</th>
<th>Name</th>
<th>Phone</th>
<th>Email</th>
<th><i>Actions</i></th>
</tr>

<c:forEach var="m" items="${manufacturers}">
<tr>
<td>${m.manufacturerId}</td>
<td>${m.name}</td>
<td>${m.phone}</td>
<td>${m.email}</td>
<td><a href="TestServlet?action=remove&id=${m.manufacturerId}">Delete</a></td>
</tr>
</c:forEach>
</table>

</body>
</html>

 Notice also that we did not use any kind of pagination here, whereas the method findByRange() exists in the facade. You may try to add pagination to your JSP/Servlet too.

Play with CDI, add injectable beans to the project, use of CDI qualifiers

We propose here to the Context Dependency Injection feature of Java EE6 by creating some injectable beans for decorating the list of manufacturers.

Enable CDI in your project

In order to enable CDI in a project, you must add a beans.xml file to the WEB-INF directory of your project. This file can be empty, this will be enough for enabling CDI, but a more "standard" version is preferable, as we will add things to it later on...

WEB-INF/beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

If you get null pointers exceptions in your project, maybe you forgot to add this beans.xml file or you put it in a wrong place.

Create an injectableBeans package, add a TextDecorator interface

In a package named "injectableBeans", add a new interface named TextDecorator that looks like that:

package injectableBeans;

public interface TextDecorator {
public String decorate(String text);
}

The beans that will implement this method will all have a decorate() method that takes a String as input and returns the same String but "decorated", for example, in bold or in italic...

Add a beans that implements the TextDecorator interface, names BoldMaker

package injectableBeans;


public class BoldMaker implements TextDecorator {
public String decorate(String text) {
return "<B>" + text + "</B>";
}
}

This bean is just a plain Java class. However, when one creates a java bean, it is the same as having the @Dependant annotation. Thi "default scope" means that the bean, when injected,  is injected directly. For non-default "
scopes (such as @ApplicationScope, @SessionScope, etc.) , a proxy to the bean is injected. The actual bean instance is injected at  runtime after the scope and context is determined correctly. 

Inject the bean in the Servlet, test the project

In the Servlet, inject the previous bean using the @Inject annotation.

@Inject
TextDecorator decorator;

Then in the displayManufacturers() method, modify the code to decorate in bold the name of each manufacturer before sending the list to the JSP for display.

private void displayManufacturers(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// delegate business to the session bean facade
Collection<Manufacturer> manufacturers = mf.getManufacturers();

// INJECTED DECORATOR USE HERE !
for(Manufacturer m: manufacturers) {
m.setName(decorator.decorate(m.getName()));
}

// Put data to display on the request
request.setAttribute("manufacturers", manufacturers);

// number of manufacturers in the database
request.setAttribute("count", mf.count());

// Forward the request to the view. Servlet and JSP are parts of same request
RequestDispatcher dispatcher = request.getRequestDispatcher("DisplayManufacturers.jsp");
dispatcher.forward( request, response );
}

Run the project, you should see that the names of each manufacturer is in bold in the table.

Add a second injectable bean for decorating Strings in italic

Write a second bean similar to the BoldMaker. Call it ItalicMaker this time. This one, instead of wrapping the String parameter with <B></B> it wraps it with <I></I>. The code should look like that:

package injectableBeans;

public class ItalicMaker implements TextDecorator {
public String decorate(String text) {
return "<I>" + text + "</I>";
}

}

Try to deploy the project. You should see an error like this one:

Caused By:  .... Ambiguous ... dependencies for type [TextDecorator] with qualifiers [@Default] at injection point... blah blah...

The error message clearly explains that the TextDecorator interface has two implementations, both with the default set of qualifiers. The CDI runtime finds both the implementations equally capable for injection and gives an error message explaining the “ambiguous dependencies”.

Lets resolve this by adding a qualifier on the implementations. We will add a qualifier to both of them while only one is needed for disambiguation (the one without qualifier has the @Default qualifier, by default)

Create two qualifier annotations, qualify the beans, qualify the injection

Qualifier are special annotations, there is no code really inside them, they are more "markers" or user friendly lable that will help us (and the CDI framework) identify different implementations of a same interface.

Add these two classes that defines the @BOLD and @ITALIC qualifiers in the "qualifiers" package:

BOLD.java

package qualifiers;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import javax.inject.Qualifier;


@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface BOLD {
}

ITALIC.java

package qualifiers;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import javax.inject.Qualifier;


@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface ITALIC {
}

Then, we just add this qualifier to our implementations of the TextDecorator interface:

BoldMaker.java

package injectableBeans;

import qualifiers.BOLD;

@BOLD
public class BoldMaker implements TextDecorator {
public String decorate(String text) {
return "<B>" + text + "</B>";
}
}

ItalicMaker.java

package injectableBeans;

import qualifiers.ITALIC;

@ITALIC
public class ItalicMaker implements TextDecorator {
public String decorate(String text) {
return "<I>" + text + "</I>";
}
}

Now, we "qualified" the implementations. Let's use the qualifier also in the Servlet, in order to indicate which implementation we would like:

TestServlet.java

	@Inject @BOLD
TextDecorator decorator;

or

	@Inject @ITALIC
TextDecorator decorator;

Run the project, try both possibilities... you see how it works ! Nice isn't it ?

Using interceptors

The Interceptors do what they say – they intercept on invocation and lifecycle events on an associated target class. They are used to implement things like logging and auditing.
The CDI provides an annotation-based approach to binding interceptors to beans. The target class may specify multiple interceptors thus forming a chain of interceptors.

This section will explain how to add an interceptor that intercepts all the business method  invocations of the facade EJB we created  and logs them.

Add an interceptor binding type annotation to the project named @Logging

We will create a new annotation named @Logging that we will just add before a class definition or before a method for intercepting and method invocations. This is similar to the annotations we created for the BOLD and ITALIC qualifiers, but there are some particularities.

Add this class in a new package called "interceptors":

package interceptors;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Inherited
@InterceptorBinding
@Retention(RUNTIME)
@Target({ METHOD, TYPE })
public @interface Logging {
}

This time, we did not define a qualifier but an "InterceptorBinding", as the meta annotation @InterceptorBinding shows. Meta annotations are annotations on annotations. Notice also that the @TARGET meta annotation indicates that this new @Logging annotation we are defining could only apply on METHODS and TYPES (classes).

Add an interceptor that implements the @Logging annotation/interface

We are going now to implement (or bind) an interceptor by creating a POJO class that looks like that:

package interceptors;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@Logging
public class LoggingInterceptor {
@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
System.out.println("----------------------------");
System.out.println("BEFORE: " + context.getMethod());

System.out.print("Input Parameters values: ");
// Prints the parameter values of the method
Object[] parameters = context.getParameters();
for(Object o: parameters) {
System.out.print(o + " ,");
}
System.out.println();


// Call the intercepted method
Object result = context.proceed();

System.out.println("Output Parameter value: " + result);


System.out.println("AFTER: " + context.getMethod());

return result;
}
}

Hmmm most of the code are println, so don't panic ! What can we notice:

  1. This class is annotated by @Interceptor (it is an interceptor) and @Logging (it is a Logging Interceptor); This means that classes or methods preceded by @Logging will be intercepted by this interceptor,
  2. The @AroundInvoke annotation can be only used once. It annotated the "interception method". This method a single parameter, the InvocationContext.
  3. This context object proposes a set of methods that are very much related to the Java reflexivity API. For example we can get a Method object using getMethod, an array of objects corresponding to the parameters using getParameters, an array of classes corresponding to the parameter types with GetParameterTypes(), etc. The example prints the input parameter names values passed to the method,
  4. The context.proceed() method invokes the method that has been intercepted, passing the input parameters (notice, that there is a context.setParameters() method, so it is possible to alter them during the interception), and the return value is also intercepted,
  5. The return value is printed, 
  6. and returned so that clients get it.

Update the beans.xml

The interceptors may be specified on the target class using the @Interceptors annotation which suffers from stronger coupling to the target class and not able to change the ordering globally.

The recommended way to enable interceptors is by specifying them in the beans.xml file.

Add these lines to beans.xml to declare the interceptor:

<interceptors>
    <class>org.samples.LoggingInterceptor</class>
</interceptors>

Annotated the ManufacturerFacade for logging, run the project

We are nearly done. In order to bind the interceptor to a java bean target class, add a @Logging annotation to the ManufacturerFacade class, just before the class definition (or you may try later on to add this annotation just before some methods).

@Stateless
@LocalBean
@Logging
public class ManufacturerFacade { ... }

Run the project, look at the console ! You should see things like that:

BEFORE: public java.util.List sessions.ManufacturerFacade.getManufacturers()
Input Parameters values:
[EL Info]: 2012-10-15 19:18:09.319--ServerSession(9416078)--EclipseLink, version: Eclipse Persistence Services - 2.3.2.v20111125-r10461 ...
Output Parameter value: [entities.Manufacturer@a66840, entities.Manufacturer@1064943, entities.Manufacturer@1b3f63d, ... AFTER: public java.util.List sessions.ManufacturerFacade.getManufacturers()
----------------------------
BEFORE: public long sessions.ManufacturerFacade.count()
Input Parameters values:
Output Parameter value: 30
AFTER: public long sessions.ManufacturerFacade.count() ----------------------------
BEFORE: public entities.Manufacturer sessions.ManufacturerFacade.create(int,java.lang.String,java.lang.String,java.lang.String)
Input Parameters values: 4 ,buffa ,090876 ,toto@toto.fr ,
Output Parameter value: entities.Manufacturer@b7cf67
AFTER: public entities.Manufacturer sessions.ManufacturerFacade.create(int,java.lang.String,java.lang.String,java.lang.String)

 Well done ! You have now a beautiful looging service that works with annotations !

To go further, we recommend reading a good course/tutorial on java reflexivity API, like this one by Michel Buffa (in french: http://deptinfo.unice.fr/twiki/pub/M...minfo-0910.pdf) and look at the official section of the JavaEE6 tutorial by Oracle: http://docs.oracle.com/javaee/6/tuto...doc/gkeed.html

An excellent quick overview of CDI goodies is proposed in this blog entry (in french): http://blog.xebia.fr/2012/02/01/java...bien-se-tenir/

Mots clés:
 
Images (6)
Voir 1 - 6 sur 6 images | Voir tout
Aucune description
explodedA...  Actions
Aucune description
JPASelect...  Actions
Aucune description
persisten...  Actions
Aucune description
jpa facet...  Actions
Aucune description
javaDBDat...  Actions
Aucune description
javaDBInE...  Actions
Commentaires (0)
Vous devez être connecté pour poster un commentaire.