Travel Agency Demo

Persistence with JPA

Christina Lin

Environment Setup

Install H2 database
Copy travelagency.mv.db to ~/h2 folder in your machine.

 

Setup Fabric authentication
Go to ~/.m2/settings.xml
Unser servers, add the following id/pwd information
    <server>
      <id>fabric8.upload.repo</id>
      <username>admin</username>
      <password>admin</password>
    </server>

 

You can skip this part if you already done so

You can skip this part if you already done so

Setup JBoss Fuse

  • Unzip you JBoss Fuse, (please use the one provided)
  • Startup JBoss Fuse, by going to INSTALL_PATH/jboss-fuse-6.1.1.redhat-412/bin and start it
    • Linux
      • ./fuse
    • Windows
      • fuse.bat
  • In console create Fuse Fabric by typing 
    • fabric:create --wait-for-provisioning
  • Stop your fuse by typing exit in console

Create booking service, we are going to provide 2 functions in this service, Hotel booking and the other one is to cancel it. We are going to interact with our persistence layer with JPA. 

Choose the blueprint archetype, 

GroupID: org.blogdemo.travelagency
ArtifactID: hotelbookingservice

 

Create a Fuse project,

Open up /example/src/main/resources/OSGI-INF/blueprint/blueprint.xml and remove the route, so you are left with a blank canvas. 

Remove the hellobean registry in the xml file. And delete all the java files.

<bean id="helloBean" class="com.mycompany.camel.blueprint.HelloBean">        
<property name="say" value="Hi from Camel"/>
</bean>​

Remove everything under

src/java/* and src/test/*

Setup the dependencies in pom.xml

<!-- Database persistence-->
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jpa</artifactId>
  <version>2.12.0.redhat-610379</version>
</dependency>
<dependency>
  <groupId>org.apache.openjpa</groupId>
  <artifactId>openjpa</artifactId>
  <version>2.3.0</version>
</dependency>  

 

<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.4</version>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>1.4.181</version>
</dependency>

<!--Messaging from and to AMQ -->
<dependency>
  <groupId>org.apache.activemq</groupId>
  <artifactId>activemq-camel</artifactId>
  <version>5.9.0.redhat-610379</version>
</dependency>

Go back Camel route file, blueprint.xml, add activemq setting to connect to our messaging queue

<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
  <property name="brokerURL" value="tcp://localhost:61616"/>    
  <property name="userName" value="admin"/>
  <property name="password" value="admin"/>
</bean>

 

Inject JPA EntityManager and Transaction manager into camel context, and assign them to your Camel JPA component.

<bean id="jpa" class="org.apache.camel.component.jpa.JpaComponent">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
  <property name="transactionManager" ref="jpaTxManager"/>
</bean>
<bean id="jpaTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" init-method="afterPropertiesSet">   
  <property name="persistenceUnitName" value="travelagency"/>
  <property name="jpaDialect">
    <bean class="org.springframework.orm.jpa.vendor.OpenJpaDialect" />
  </property>
</bean>
<bean id="entityManagerFactory" factory-ref="entityManagerFactoryBean" factory-method="getObject" /> 

With JPA, the most important part is the Entity itself, so create two entities.

Create an Entity
Package: org.blogdemo.travelagency.hotelbookingservice
Class name: Booking

 

package org.blogdemo.travelagency.hotelbookingservice;

import static javax.persistence.LockModeType.PESSIMISTIC_WRITE;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@NamedQuery(name = "queryHotelBookingById", query = "select hotelbooking from Booking hotelbooking where hotelbooking.bookingid=:bookingid " , lockMode = PESSIMISTIC_WRITE)
@Table(name = "hotelbooking")
public class Booking {    
    @Column(name = "bookingid")
    @Id
    String bookingid;
    
    

@Column(name = "recieveDate")
    Date recieveDate;

    public String getBookingid() { 

       return bookingid; 

     }

    public void setBookingid(String bookingid) {

        this.bookingid = bookingid;

     }

    public Date getRecieveDate() {

         return recieveDate;

      }

    public void setRecieveDate(Date recieveDate) {

         this.recieveDate = recieveDate;

     }

}

Create another entity
Package: org.blogdemo.travelagency.hotelbookingservice
Class name: CancelBooking

 

package org.blogdemo.travelagency.hotelbookingservice;
import java.util.Date;
import javax.persistence.*;
@Entity
@Table(name = "cancelhotelbooking")
public class CancelBooking {
    @Column(name = "bookingid")
    @Id
    String bookingid;
    

@Column(name = "recieveDate")
    Date recieveDate;

    

public String getBookingid() { bookingid;
    public void setBookingid(String bookingid) {
        this.bookingid = bookingid;
    }
    public Date getRecieveDate() { recieveDate;}
    public void setRecieveDate(Date recieveDate) {
        this.recieveDate = recieveDate;
    }
}

Same as in normal JPA project, we will need to declare the Entities, create a xml file under resource/META-INF called

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
             version="2.0">
  <persistence-unit name="travelagency" transaction-type="RESOURCE_LOCAL">
    <class>org.blogdemo.travelagency.hotelbookingservice.Booking</class>
    <class>org.blogdemo.travelagency.hotelbookingservice.CancelBooking</class>
    <properties>
          <property name="openjpa.ConnectionDriverName" value="org.h2.Driver" />
          <property name="openjpa.ConnectionURL" value="jdbc:h2:file:~/h2/travelagency;AUTO_SERVER=TRUE" />

     <property name="openjpa.ConnectionUserName" value="sa" />
     <property name="openjpa.ConnectionPassword" value="" />
     <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(SchemaAction='refresh')"/>
     <property name="openjpa.Log" value="DefaultLevel=INFO, Runtime=INFO, Tool=INFO, SQL=TRACE"/>
     <property name="openjpa.jdbc.DBDictionary" value="h2(useSchemaName=true)"/>
     </properties>
 </persistence-unit> </persistence>

For booking, because we are inserting a new POJO, we are going to need a service to create these entities. This service also handles the necessary business logic in the booking service, such as generating random booking id and calculating fees.0

Create a java class
Package: org.blogdemo.travelagency.hotelbookingservice

Class name: BookingService

package org.blogdemo.travelagency.hotelbookingservice;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Calendar;
import java.util.Random;

public class BookingService {
    private SecureRandom secureRandom = new SecureRandom();

    
    

public Booking createBooking(String someStringFromBPMS){
        Calendar cal = Calendar.getInstance();
        Booking booking = new Booking();
        booking.setBookingid(genBookingId());
        booking.setRecieveDate(cal.getTime());
        return booking;
    }

public CancelBooking createCancelBooking(String id){
        Calendar cal = Calendar.getInstance();
        CancelBooking cancelbooking = new CancelBooking();
        cancelbooking.setBookingid(id);
        cancelbooking.setRecieveDate(cal.getTime());
        return cancelbooking;
    }
    
    private String genBookingId(){
               return new BigInteger(130, secureRandom).toString(32);
    }
    
    public int cancelCharge(){
        final Random random = new Random();
        return random.nextInt((10-5)+1) + 5;
    }
}

 

Register the service bean in camel context. 

<bean id="bookingService" class="org.blogdemo.travelagency.hotelbookingservice.BookingService" />
<bean id="params" class="java.util.HashMap" />

 

Create the route to book the hotel

  • Activemq Endpoint
    • Uri: activemq:queue:hotelbooking
  • Bean
    • Bean Name: bookingService
    • Method: createBooking
  • JPA endpoint
    • Uri: jpa://org.blogdemo.travelagency.hotelbookingservice.Booking?consumeDelete=false
  • Log Endpoint:
    • Message: ${body}
  • setBody
    • Expression:OK
    • Language: constant

Create the route to cancel booking, 

  • Activemq Endpoint
    • Uri: activemq:queue:cancelhotelbooking
  • Bean
    • Bean Name: params
    • Method: put('bookingid',${body})
  • PollEnrich Endpoint
    • Timeout: 2000
    • Uri: jpa://org.blogdemo.travelagency.hotelbookingservice.Booking
    • consumeDelete=false&consumer.namedQuery=​queryHotelBookingById&consumer.parameters=#params
  • Log
    • Message: try:[${body}]
  • Choice
    • When
      • Expression: ${body} != null
      • Language: simple
      • Bean
        • Bean Name: bookingService
        • Method: createCancelBooking(${body.bookingid})
      • JPA endpoint
        • Uri: jpa://org.blogdemo.travelagency.hotelbookingservice.CancelBooking?consumeDelete=false
      • setBody
        • Expression:bean:bookingService?method=cancelCharge
        • Language: simple
  • Otherwise
    • setBody
      • Expression:0
      • Language: constant

Because we are using OpenJPA as our JPA library, we need to enhance the byte-code at build time. Go to your pom.xml and add the plugin in your plugin setting

<plugin>
        <groupId>org.apache.openjpa</groupId>
        <artifactId>openjpa-maven-plugin</artifactId>
        <version>2.2.0</version>
        <configuration>
            <includes>org/blogdemo/*.class</includes>
                  <addDefaultConstructor>true</addDefaultConstructor>               
            <enforcePropertyRestrictions>true</enforcePropertyRestrictions>
        </configuration>

         <executions>
            <execution>
                <id>enhancer</id>
                <phase>process-classes</phase>
                <goals>
                    <goal>enhance</goal>
                </goals>
            </execution>
        </executions>
        <dependencies>
            <dependency>
                <groupId>org.apache.openjpa</groupId>
                <artifactId>openjpa</artifactId>
                <version>2.3.0</version>
            </dependency>
        </dependencies>
    </plugin> 

Writing test code

Under src/test/java create 2 classes, one is the helper class to setup messaging queue and the other is the actual testing code.
Package: org.blogdemo.travelagency.hotelbookingservice
Class name: CamelJmsTestHelper

package org.blogdemo.travelagency.promtionhotel;

import java.util.concurrent.atomic.AtomicInteger;

import javax.jms.ConnectionFactory;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;


public final class CamelJmsTestHelper {

    private static AtomicInteger counter = new AtomicInteger(0);

    private CamelJmsTestHelper() {
    }

    

    public static ConnectionFactory createConnectionFactory() { return createConnectionFactory(null);}

    public static ConnectionFactory createConnectionFactory(String options) {
       int id = counter.incrementAndGet();
        String url = "vm://test-broker-" + id + "?broker.persistent=false&broker.useJmx=false";
        if (options != null) { url = url + "&" + options; }
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        connectionFactory.setCopyMessageOnSend(false);
        connectionFactory.setOptimizeAcknowledge(true);
        connectionFactory.setOptimizedMessageDispatch(true);        
        connectionFactory.setUseAsyncSend(false);

        connectionFactory.setAlwaysSessionAsync(false);
        PooledConnectionFactory pooled = new PooledConnectionFactory(connectionFactory);
        pooled.setMaxConnections(8);
        return pooled;
    }
}

Package: org.blogdemo.travelagency.hotelbookingservice
Class name: RouteTest

 

package org.blogdemo.travelagency.hotelbookingservice;

import javax.jms.ConnectionFactory;
import static org.apache.camel.component.jms.JmsComponent.jmsComponentAutoAcknowledge;

import org.apache.camel.CamelContext;
import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
import org.junit.Test;

public class RouteTest extends CamelBlueprintTestSupport {
     protected String componentName = "activemq";
    @Override
    protected String getBlueprintDescriptor() { return "/OSGI-INF/blueprint/blueprint.xml"; }
       protected CamelContext createCamelContext() throws Exception {
        CamelContext camelContext = super.createCamelContext();

        ConnectionFactory connectionFactory = CamelJmsTestHelper.createConnectionFactory();
        camelContext.addComponent(componentName, jmsComponentAutoAcknowledge(connectionFactory));

        return camelContext; }

    @Test
    public void testBooking() throws Exception {
        String out = template.requestBody("activemq:queue:hotelbooking", "12345", String.class);
        assertTrue(out.length()==26);        
    }
    @Test
    public void testSuccessfulCancelBooking() throws Exception {
        String bookingId = template.requestBody("activemq:queue:hotelbooking", "12345", String.class);
        Integer out = template.requestBody("activemq:queue:cancelhotelbooking", bookingId, Integer.class);
        assertTrue(out > 0);        
    }

    @Test
    public void testProblemCancelBooking() throws Exception {
        Integer out = template.requestBody("activemq:queue:cancelhotelbooking", "12345", Integer.class);
        assertTrue(out==0);        
    }
}

Right click on the RouteTest.java and select JUnit Test

You should see all three test has passed in the Junit Test view

Lab Complete!

Travel Agency Demo - JPA

By weimeilin

Travel Agency Demo - JPA

  • 5,233