Sunday, June 25, 2017

JavaEE: Controlling Concurrent Access to Entity Data with Locking

On a previous post, I showed how to make a database connection and get data from a relational database using the persistence unit.  We can do all CRUD database operation.  Nevertheless, this is not good enough for any application without a transaction process.

Transaction is a sequence of operations performed as a single logical unit of work.  A logical unit of work must fulfill the four properties of a database operation to qualify as a transaction: Atomicity, Consistency, Isolation, and Durability.

I will start with a common transaction problem and explain how to prevent problems.  The pom.xml for this project is same, and an Account class definition is only added to the persistence.xml shown on another post.  It means the JTA container managed transaction is also used.
  
Account Entity
@Entity
@NamedQuery(name = "Account.findAll", query = "SELECT a FROM Account a")
public class Account implements Serializable {
   private static final long serialVersionUID = 6447459012451754056L;

   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "account_id_seq")
   @SequenceGenerator(name = "account_id_seq", sequenceName = "ACCOUNT_ACCOUNT_ID_SEQ")
   @Column(name = "account_id")
   private Long accountId;

   @Column(name = "account_number")
   private Integer accountNumber;

   @Min(0)
   @Column(name = "balance")
   private Integer balance;

   @Column(name = "create_time")
   private Timestamp createTime;

   @Column(name = "nick_name")
   private String nickName;

   @Column(name = "update_time")
   private Timestamp updateTime;

RESTful Web Service Endpoint
@Logging
@Path("/account")
public class AccountRestEnd {
   @Inject
   private AccountService accountService;
   
   @POST
   @Path("/balance")
   public Response changeBalance(@QueryParam("id") Long acctId,
         @DefaultValue("0") @QueryParam("amount") Integer amount){
      Account acct = accountService.updateBalance(acctId, amount);
      return Response.ok(acct).build();
   }
}

Web Service Database Operation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Logging
@Stateless
public class AccountService {
   @PersistenceContext(name="demoDB")
   private EntityManager em;
   
   public Account updateBalance(Long id, Integer amountChange){
      Account acct = em.find(Account.class, id);
      Integer currentBalance = acct.getBalance();
      acct.setBalance(currentBalance + amountChange);
      //Updating the updateTime field is omitted on purpose 
      return acct;
   }
}

Initially, I created two accounts in a database.

We can now update a balance using a post operation of http://local;host:8080/demo/account/balance?id=1&amount=8

You can run many post operations with a different amount value, and everything seems good until you realize a serious transaction problem in the production environment.  You may think the JTA manages all commit and rollback transactions including the table locking, but it is not true.

Problem with Concurrent Access
To simulate the problem of the concurrent access, let's put a breakpoint on a line 10 of the AccountService, and restart the server with a debug mode. Default debug port is a 5005.
   mvn clean package tomee:debug

Now, two POST operations are triggered to deposit 5 dollar and 10 dollars.
http://localhost:8080/demo/account/balance?id=1&amount=5
http://localhost:8080/demo/account/balance?id=1&amount=10

Now, two processes with a different server side thread will be stoped at the line 10 of the AccountService.  The currentBalance of two processes are same: 18.  Then, you can run two processes one at a time.  Depends on which process was run later, the final balance is either 23 or 28 although you deposited total 15 with two operations.

Locking
Persistence providers use two different locking mechanism: optimistic locking, which is a default, and pessimistic locking, which may be better when the underlying data is frequently accessed and modified by many transaction.  Lock modes may be specified to increase the level of optimistic locking or to request the use of pessimistic locks.

Lock Modes for Concurrent Entity Access 
Lock Mode
Description
OPTIMISTIC
Obtain an optimistic read lock for all entities with version attributes.
OPTIMISTIC_FORCE_INCREMENT
Obtain an optimistic read lock for all entities with version attributes, and increment the version attribute value.
PESSIMISTIC_READ
Immediately obtain a long-term read lock on the data to prevent the data from being modified or deleted. Other transactions may read the data while the lock is maintained, but may not modify or delete the data.
The persistence provider is permitted to obtain a database write lock when a read lock was requested, but not vice versa.
PESSIMISTIC_WRITE
Immediately obtain a long-term write lock on the data to prevent the data from being read, modified, or deleted.
PESSIMISTIC_FORCE_INCREMENT
Immediately obtain a long-term lock on the data to prevent the data from being modified or deleted, and increment the version attribute of versioned entities.
READ
A synonym for OPTIMISTIC. Use of LockModeType.OPTIMISTIC is to be preferred for new applications.
WRITE
A synonym for OPTIMISTIC_FORCE_INCREMENT. Use of LockModeType.OPTIMISTIC_FORCE_INCREMENT is to be preferred for new applications.
NONE
No additional locking will occur on the data in the database.

This table is from https://docs.oracle.com/javaee/7/tutorial/persistence-locking002.htm and this page shows how to set the lock mode. I will use one below

Pessimistic Lock to the AccountService
On the line 8 of the AccountService code, I add a LockModeType as the third parameter of the find method.  Then, put a breakpoint on the line 8 and the 10.

8
     Account acct = em.find(Account.class, id, LockModeType.PESSIMISTIC_WRITE);

Then, let's trigger two POST operations again.
http://localhost:8080/demo/account/balance?id=1&amount=9
http://localhost:8080/demo/account/balance?id=1&amount=4

Both threads stop at the line 8.  Run one thread until the line 10. Then, find another thread shown on IDE and run.  This thread hangs: The Account entity was blocked and wait.  Go to the first thread and execute the process.  After the first thread is completed, the second thread will go to the next line.

The pessimistic lock obtains a a long term lock on the data.

Optimistic Lock

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Logging
@Stateless
public class AccountService {
   @PersistenceContext(name="demoDB")
   private EntityManager em;
   
   public Account updateBalance(Long id, Integer amountChange){
      Account acct = em.find(Account.class, id);
      em.lock(acct,  LockModeType.OPTIMISTIC);
      Integer currentBalance = acct.getBalance();
      acct.setBalance(currentBalance + amountChange);
      //Updating the updateTime field is omitted on purpose 
      return acct;
   }
}

You can run the same test cases with this OPTIMISTIC Lock type, but you may get this kind of error even with one POST request:

org.hibernate.OptimisticLockException: [OPTIMISTIC] not supported for non-versioned entities [demo.model.entity.Account]

Before committing changes to the data, the persistence provider checks that no other transaction has modified or deleted the data since the data was read.  This is accomplished by a version column in the database table with a corresponding version attribute in the entity class.  The original transaction checks the version attribute and an OptimisticLockException will be thrown if the data has been modified by another transaction.  Then, the original transaction will be rolled back.

The error shown above is caused by the non-existence of the version column.  Now, I added a version_number column to the account table and added a versionNumber field with a @Version annotation to the Account entity.



   @Version
   @Column(name = "version_number")
   protected Integer versionNumber = 0;

The @Version annotation has the following requirements:
  • Only a single @Version attribute may be defined peer entity
  • The @Version attribute must be in the primary table for an entity mapped to multiple tables.
  • The attribute must be one of these: int, Integer, long, Long, short, Short, or java.sql.Timestamp

When you run the same test cases with two threads as shown with the pessimistic lock, a thread will not hang.  Since the optimistic lock is a default lacking mechanism, we don't need to specify a locking mode.  Both thread will run without blocking, but one thread will throw an exception if another thread already updated the Account object.  The exception will be thrown inside a JavaEE container.

   public Account updateBalance(Long id, Integer amountChange){
      Account acct = em.find(Account.class, id);
      Integer currentBalance = acct.getBalance();
      acct.setBalance(currentBalance + amountChange);
      //Updating the updateTime field is omitted on purpose 
      return acct;
   }

TomEE uses an Apache Geromino for the transaction implementation. A TransactionImpl class catches an OptimisticLockException in a commit method and throws an RollbackException. Another exception can be thrown during the process and you can catch an exception after the EJB AccountService method is completed.

   @POST
   @Path("/balance")
   public Response changeBalance(@QueryParam("id") Long acctId,
         @DefaultValue("0") @QueryParam("amount") Integer amount){
      Account acct = null;
      try{
         acct = accountService.updateBalance(acctId, amount);
      }catch(Exception ex){
         return Response.status(Response.Status.CONFLICT).build();
      }
      return Response.ok(acct).build();
   }
   

Wednesday, June 21, 2017

JavaEE: JAX-RS - Extracting Values from a URI or Request Header

Extracting Java type of a Request or Response
The javax.ws.rs.core.Context annotation retrieves the Java types related to a request or response and the javax.ws.rs.core.HttpHeaders interface provides information about request headers and cookies.
The RESTful web services need a versioning as the codes are evolving, and putting a version number in the http header is one of ways of doing it.

Extracting Form Data
The @FormParam annotation is used to extract for parameters from HTML forms.

Extracting Path Parameters
The @PathParam annotation lets you use variable UPI path fragments when you call a method.

Extracting Query Parameters
The @QueryParam annotation is used to extract query parameters from the queries of the request URI.  The @DefaultValue annotation defines a default value, which is used if no value is provided for the query parameters.  By default JAX-RS assigns a null value for Object values and zero for primitive data types.

Example Code

@Logging
@Path("/resource")
public class ResourceRestEnd {
   @GET
   public String getHeader(@Context HttpHeaders hh){
      MultivaluedMap<String, String> headerParam = hh.getRequestHeaders();
      return headerParam.getFirst("user-agent");
   }
   
   @GET
   @Path("/name/{lastname}/{firstname}")
   public String getPathParams(@PathParam("lastname") String lastName,
                               @PathParam("firstname") String firstName){
      return "Hello " + firstName + " " + lastName;
   }
   
   @GET
   @Path("/name")
   public String getQueryParams(@DefaultValue("NO_LAST") @QueryParam("lastname") String lastName,
                               @DefaultValue("NO_FIRST") @QueryParam("firstname") String firstName){
      return "Hello " + firstName + " " + lastName;
   }
   
   @POST
   @Consumes(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
   public void post(MultivaluedMap<String, String> formParams){
      List<String> val = formParams.get("some_key");
   }
}

Results

http://localhost:8080/demo/resource

This is the value in the MultivaluedMap when a breakpoint using an IDE is used.
{Accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8], accept-encoding=[gzip, deflate, sdch, br], accept-language=[en-US,en;q=0.8,ko;q=0.6], cache-control=[max-age=0], connection=[keep-alive], Content-Type=[null], host=[localhost:8080], upgrade-insecure-requests=[1], user-agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36]}

This is the the header values shown on a web browser.


http://localhost:8080/demo/resource/name/Smith/Tom

http://localhost:8080/demo/resource/name?lastname=Smith&firstname=Tom


Sunday, June 18, 2017

JavaEE: EntityManager, PersistenceContext, Data from Database

On the previous post, we learned how to define a persistence unit and DataSource on TomEE.  On this post, we will see how to make a database connection and get data from a relational database using the persistence unit.

A persistence context is a set of managed unique entity instances, and the entity instances and their lifecycle are managed by the entity manager within the persistence context.  A container-managed entity manager's lifecycle is managed by the Java EE container and an application-managed entity manager's lifecycle is managed by the application.

The entity manager for a persistence context is obtained from an entity manager factory.  When container-managed entity managers are used (in Java EE environments), the application does not interact with the entity manager factory. The entity managers are obtained directly through dependency injection or from JNDI, and the container manages interaction with the entity manager factory transparently to the application.  An entity manager must not be shared among multiple concurrently executing threads, as the entity manager and persistence context are not required to be threadsafe. Entity managers must only be accessed in a single-threaded manner. [JPA 2.1 Specification]

Obtaining a (container-managed) Entity Manager
The PersistenceContext annotation is used for entity manager injection with a name 'demoDB', which is a persistence-unit name specified in the persistence.xml. This is a UserService class with an entity manager injected.

@Logging
@Stateless
public class UserService {
   @Inject
   private Logger logger;
   
   @PersistenceContext(name="demoDB")
   private EntityManager em;
   
   public List<User> getAllUsers(){
      Query query = em.createNamedQuery("User.findAll");
      return query.getResultList();
   }
   
   public User getUser(Long id){
      Query query = em.createQuery("SELECT u From User u WHERE u.id = :id");
      query.setParameter("id", id);
      
      User user = null;
      try{
         user = (User)query.getSingleResult();
      }catch(NoResultException ne){
         logger.info(ne.getMessage() + " for an input id " + id);
      }
      return user;
   }
}

In addition to the @PersistenceContext annotation, an interceptor @Logging annotation and an injected Logger object are also used.

User Entity Object
The UserService class uses a User entity object that maps to a 'Users' database table in a PostgreSQL database.  This is a User class without the getter/setter methods to save the space.  Many IDEs such as an Eclipse provide a tool to create the Entity classes after connecting to a database.  This User class was also defined in the persistence.xml from the previous post.

@Entity
@Table(name="users")
@NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="user_id_seq")
   @SequenceGenerator(name="user_id_seq", sequenceName="USERS_USER_ID_SEQ") 
   @Column(name="user_id")
   private Long userId;

   @Column(name="birth_day")
   private Integer birthDay;

   @Column(name="birth_month")
   private Integer birthMonth;

   @Column(name="birth_year")
   private Integer birthYear;

   @Column(name="create_time")
   private Timestamp createTime;

   @Column(name="first_name")
   private String firstName;

   @Column(name="last_name")
   private String lastName;

   @Column(name="update_time")
   private Timestamp updateTime;

   @Column(name="user_name")
   private String userName;

RESTful web service
This is a web service entry point that uses the UserService.  The return type is a Response abstract class in a javax.ws.rs.core package and the TomEE provides a ResponseImpl class from an Apache CXF library.  Therefore, adding any additional library is not required.

@Logging
@Path("/user")
public class UserRestEnd {
   @Inject
   private UserService userService;
   
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response getAllUser(){
      List<User> users = userService.getAllUsers();
      Response res = Response.status(Response.Status.OK).entity(users).build();
      return res;
   }
   
   @GET
   @Path("/{id}")
   @Produces(MediaType.APPLICATION_JSON)
   public Response getUser(@PathParam("id") Long id){
      User user = userService.getUser(id);
      Response res = Response.status(Response.Status.OK).entity(user).build();
      return res;
   }
}

Start a TomEE-maven-plugin and available APIs shown on logs
  mvn clean package tomee:run

When the server starts, the TomEE logs show available RESTful APIs.

org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints      Service URI: http://localhost:8080/demo/user      -> Pojo demo.rest.endpoint.UserRestEnd
org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8080/demo/user/     ->      Response getAllUser() 
org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8080/demo/user/{id} ->      Response getUser(Long)

Result



 







Logs from the interceptor @Logging
There are no log on the UserRestEnd class and the UserService, but the @Logging annotation will create the logs with an execution time.

demo.interceptor.LoggingInterceptor.logMethod  Entering demo.rest.endpoint.UserRestEnd - getAllUser
demo.interceptor.LoggingInterceptor.logMethod  Entering demo.rest.service.UserService - getAllUsers
demo.interceptor.LoggingInterceptor.logMethod  Exiting  demo.rest.service.UserService - getAllUsers Execution Time: 2ms
demo.interceptor.LoggingInterceptor.logMethod  Exiting  demo.rest.endpoint.UserRestEnd - getAllUser Execution Time: 3ms


Friday, June 16, 2017

JavaEE: JPA persistence.xml and DataSource on TomEE

All applications need to create, retrieve, update, and delete data, and most applications use a relational database.  In the JDBC API, database are accessed by using DataSource object, which may be registered with a JNDI naming service.  Applications that use the JPA specify the DataSource object in the jta-data-source element of the persistence.xml file.

By default, TomEE uses the OpenJPA, but the hibernate is more commonly used in the software industry. So, I will use the hibernate and the PostgreSQL database.

persistence.xml
The JPA requires a persistence unit defined in a persistence.xml, which has to be located in the META-INF directory.  In the maven project created with the default directory structure, the META-INF directory is located under the src/main/resources directory.  One persistence.xml file can have more than one persistence unit definition, but I use one persistence unit here.  The persistence-unit demoDB uses a JTA for the transaction.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 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://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="demoDB" transaction-type="JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

    <jta-data-source>demoDS</jta-data-source>
    <class>demo.model.entity.User</class>
    
    <shared-cache-mode>NONE</shared-cache-mode> 
    <properties>
      <property name="hibernate.dialect" value="PostgreSQL9"/>
      <property name="hibernate.show_sql" value="true" />
      <property name="hibernate.format_sql" value="true" />
      
      <property name="tomee.jpa.factory.lazy" value="true" />
      <property name="hibernate.temp.use_jdbc_metadata_defaults" value="false" />
    </properties>
  </persistence-unit>
</persistence>

On this file, I defined a jta-data-source element 'demoDS' but where did we define the DataSource?

resources.xml on TomEE
On the TomEE, a DataSource can be defined in the <TomEE-HOME>/conf/tomee.xml file or in a WEB-INF/resources.xml.  This is my Resource declaration in the resources.xml, but more properties, which are optional, are explained in a TomEE website: http://tomee.apache.org/datasource-config.html

<?xml version='1.0' encoding='UTF-8'?>
<resources>
  <Resource id="demoDS" type="javax.sql.DataSource">
    jdbcDriver = org.postgresql.Driver
    jdbcUrl = jdbc:postgresql://localhost:5555/demoDB
    password = password
    passwordCipher = PlainText
    userName = postgres
  </Resource>
</resources>

On the next post, we will make a database connection using this persistence unit.

pom.xml used
This is very minimal pom.xml to save the space.  Depends on your needs, you may include more hibernate dependencies.  A reference on a TomEE webpage: http://tomee.apache.org/tomee-and-hibernate.html
<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>com.jihwan.jee</groupId>
  <artifactId>demo</artifactId>
  <version>0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <properties>
    <tomee.version>7.0.2</tomee.version>
    <!-- openejb.javaee.api>6.0-6</openejb.javaee.api> -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.tomee</groupId>
      <artifactId>tomee-embedded</artifactId>
      <version>${tomee.version}</version>
      <scope>provided</scope>
      <exclusions>
         <exclusion>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
         </exclusion>
      </exclusions>
    </dependency>
    
    <dependency>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
       <version>9.4.1212</version>
    </dependency>
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.10.Final</version>
      <exclusions>
        <exclusion>
          <groupId>org.hibernate.javax.persistence</groupId>
          <artifactId>hibernate-jpa-2.1-api</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.jboss.spec.javax.transaction</groupId>
          <artifactId>jboss-transaction-api_1.2_spec</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>5.2.10.Final</version>
      <exclusions>
         <exclusion>
             <groupId>org.hibernate.javax.persistence</groupId>
             <artifactId>hibernate-jpa-2.1-api</artifactId>
         </exclusion>
         <exclusion>
             <groupId>org.jboss.spec.javax.transaction</groupId>
             <artifactId>jboss-transaction-api_1.2_spec</artifactId>
         </exclusion>
     </exclusions>
    </dependency>
  </dependencies>

  <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.tomee.maven</groupId>
        <artifactId>tomee-maven-plugin</artifactId>
        <version>${tomee.version}</version>
        <configuration>
          <tomeeVersion>${tomee.version}</tomeeVersion>
          <tomeeClassifier>plus</tomeeClassifier>
          
          <synchronization>
            <extensions>
              <extension>.class</extension>
            </extensions>
          </synchronization>

          <reloadOnUpdate>true</reloadOnUpdate>
          <systemVariables>
            <tomee.serialization.class.blacklist>-</tomee.serialization.class.blacklist>
          </systemVariables>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project> 

Saturday, June 10, 2017

JavaEE: Interceptors and InterceptorBinding

Interceptors are used to invoke interceptor methods on an associated target class in conjunction with method invocations or lifecycle events.  Interceptor class may be designated with @Interceptor annotation, but it is not required.  An interceptor class must have a public and no-argument constructor.  Interceptor methods within the target class or an interceptor class are annotated with one of the following annotations.

Interceptor Method Annotations
  • javax.interceptor.AroundConstruct
       Designates the method as an interceptor method that receives a callback after the target class is constructed.
  • javax.interceptor.AroundInvoke
       Designates the method as an interceptor method.
  • javax.interceptor.AroundTimeout
       Designates the method as a timeout interceptor for interposing on timeout method for enterprise bean timers.
  • javax.annotation.PostConstruct
       Designates the method as an interceptor method for post-construct lifecycle events.
  • javax.annotation.PreDestroy
       Designates the method as an interceptor method for pre-derstroy lifecycle events.

Interceptor classes have the same lifecycle as their associated target class.

Example
- Interceptor
This interceptor will be used to log each method calls and an execution time of it.  On a previous post, we created an injectable Logger object and the injection is used in this example.  A proceed method of the InvocationContext proceed to the next interceptor.  If the interceptor is the last interceptor, it triggers the target class method.

@Interceptor
public class LoggingInterceptor implements Serializable {
   @Inject
   private transient Logger logger;
   
   @AroundInvoke
   public Object logMethod(InvocationContext ict) throws Exception {
      long start = System.currentTimeMillis();
      
      logger.info(" Entering " + ict.getTarget().getClass().getName() + " - " + ict.getMethod().getName());
      
      try{
         return ict.proceed();
      }finally{
         logger.info(" Exiting  " + ict.getTarget().getClass().getName() + " - " + ict.getMethod().getName() + 
               " Execution Time: " + (System.currentTimeMillis() - start) + "ms");
      }
   }   
}

- Target class
I will use a simple Hello RESTful service, which is very same as one shown on a previous post.  To use an interceptor in a target class, an @Interceptors(LoggingInterceptor.class) annotation is used.  When you use more than one interceptor, you can use the annotation like this: @Interceptors({FirstInterceptor.class, SecondInterceptor.class})

@Interceptors(LoggingInterceptor.class)
@Path("/hello")
public class Hello {
   @Inject
   private Logger logger;
   
   @GET
   public String sayHello(){
      logger.info("Hello World Logging!");
      return"Hello RESTful world!";
   }
}

Let's run it on a TomEE maven plug in.  Pom.xml is still same as shown on this post and this post.
   mvn clean install tomee:run

- Result
Open a URL http://localhost:8080/demo/hello
Then, look at the console output.  The second line is from the sayHello method in the target class and the first & the third line is from the interceptor method.

INFO [http-nio-8080-exec-2] demo.interceptor.LoggingInterceptor.logMethod  Entering demo.rest.endpoint.Hello - sayHello
INFO [http-nio-8080-exec-2] demo.rest.endpoint.Hello.sayHello Hello World Logging!
INFO [http-nio-8080-exec-2] demo.interceptor.LoggingInterceptor.logMethod  Exiting  demo.rest.endpoint.Hello - sayHello Execution Time: 0ms

InterceptorBinding
Interceptor bindings are intermediate annotations can be used to associate an interceptor with target beans (with class level or method level).

@InterceptorBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
}

We can use this interceptor binding with the LoggingInterceptor shown above.  Simply added the @Logging annotation
@Logging
@Interceptor
public class LoggingInterceptor implements Serializable {
   @Inject
   private transient Logger logger;
   
   @AroundInvoke
   public Object logMethod(InvocationContext ict) throws Exception {
      long start = System.currentTimeMillis();
      
      logger.info(" Entering " + ict.getTarget().getClass().getName() + " - " + ict.getMethod().getName());
      
      try{
         return ict.proceed();
      }finally{
         logger.info(" Exiting  " + ict.getTarget().getClass().getName() + " - " + ict.getMethod().getName() + 
               " Execution Time: " + (System.currentTimeMillis() - start) + "ms");
      }
   }   
}

Then, interceptor classes need to be defined in the beans.xml file (shown on a previous post) with an <interceptors> element.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
    <interceptors>
      <class>demo.interceptor.LoggingInterceptor</class>
    </interceptors>
</beans>

After this, we can use the interceptor binding directly in target classes instead of an @Interceptors annotation.  The logging result is same.

@Logging
@Path("/hello")
public class Hello {
   @Inject
   private Logger logger;
   
   @GET
   public String sayHello(){
      logger.info("Hello World Logging!");
      return"Hello RESTful world!";
   }
}

Sunday, June 4, 2017

JavaEE: Dependency Injection by Using Producer Methods

Context and Dependency Injection (CDI) is one of Java EE features that helps developers make it easy to use enterprise beans.  CDI enables objects to have their dependencies provided to them automatically instead of creating a new object explicitly in the objects.

On this page, we will talk about Java EE producer method, which is one way of creating an injectable object.  This example is continuously built on the TomEE server.  Initial setting and a pom project configuration on the TomEE is explained on previous posts.

First of all, CDI uses an optional deployment descriptor named beans.xml. The beans.xml can be an empty file but should exist under the META-INF directory.  In my pom project with auto created default directory structure, the META-INF directory should be created under a src/main/resources directory.



Basic content of the bean.xml is shown here.  This is for CDI 1.1.  The bean-discovery-mode is a new element in CDI 1.1 and it informs the CDI to discover all beans, none, or only annotated ones.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
</beans>

Producer Method
A producer method is a method that generates an object that can be injected.  It enables an application to customize how CDI managed beans are created.  It provides a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.

This is a class that creates an injectable Logger object.  I use a Logger object that comes with Java, but you can use any other Logger object.  The Producer method is annotated with a @Prodecues annotation.  This is it.  Simple!

import java.util.logging.Logger;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;

public class LogProducer {
   @Produces
   public Logger createLogger(InjectionPoint injectionPoint) { 
      return Logger.getLogger( injectionPoint.getMember().getDeclaringClass().getName() );
   }
}

I will show how to inject this Logger object on a next post, but it is simple.  You can declare a Logger object variable with an @Inject annotation.

@Inject
private Logger logger;

 

Java 9: Flow - Reactive Programming

Programming world has always been changed fast enough and many programming / design paradigms have been introduced such as object oriented p...