EJB3: Entity Callbacks and Listeners Example 2

by admin ~ October 21, 2008

Following are the examples from the chapter “Entity Callbacks and Listeners” from the book “Enterprise JavaBeans 3.0″ by Bill Bruke & Richard Monson-Haefel. This example I have tried it out on JBoss AS 5.0.0.CR1. I have used Postgresql as the database. And I have used eclipse WTP as IDE.


Example2: Entity Listeners


Client.java

package com.titan.clients;
 
import com.titan.travelagent.TravelAgentRemote;
import com.titan.domain.*;
import javax.naming.Context;
import javax.rmi.PortableRemoteObject;
 
public class Client {
	public static void main(String[] args) {
		try {
			Context jndiContext = getInitialContext();
			Object ref = jndiContext
                                     .lookup("TitanCruises/TravelAgentBean/remote");
			TravelAgentRemote dao = 
                                   (TravelAgentRemote) PortableRemoteObject
					.narrow(ref, TravelAgentRemote.class);
 
			Customer cust = new Customer();
			cust.setFirstName("Bill");
			cust.setLastName("Burke");
			int pk = dao.createCustomer(cust);
 
			cust = dao.findCustomer(pk);
			cust.setFirstName("Billy");
			dao.doMerge(cust);
			dao.doFlush(cust.getId());
			dao.doRemove(cust.getId());
		} catch (javax.naming.NamingException ne) {
			ne.printStackTrace();
		}
	}
 
	public static Context getInitialContext()
			throws javax.naming.NamingException {
		return new javax.naming.InitialContext();
	}
}

jndi.properties

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=127.0.0.1:1099

TravelAgentRemote.java

package com.titan.travelagent;
 
import javax.ejb.Remote;
import com.titan.domain.Customer;
 
@Remote
public interface TravelAgentRemote {
	public int createCustomer(Customer cust);
	public Customer findCustomer(int pKey);
	public void doMerge(Customer cust);
	public void doFlush(int pKey);
	public void doRemove(int pKey);
}

TravelAgentBean.java

package com.titan.travelagent;
 
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
 
import com.titan.domain.Customer;
 
@Stateless
public class TravelAgentBean implements TravelAgentRemote {
	@PersistenceContext(unitName = "titan")
	private EntityManager manager;
 
	public int createCustomer(Customer cust) {
		System.out.println("--------------------------------------");
		System.out.println("Calling createCustomer():" + cust.getFirstName());
		System.out.println("Calling manager.persist()");
		manager.persist(cust);
		System.out.println("Ending createCustomer.");
		return cust.getId();
	}
 
	public Customer findCustomer(int pKey) {
		System.out.println("--------------------------------------");
		System.out.println("Calling findCustomer()");
		System.out.println("manager.find()");
		Customer cust = manager.find(Customer.class, pKey);
		System.out.println("Returing from findCustomer(): "
				+ cust.getFirstName());
		return cust;
	}
 
	public void doMerge(Customer cust) {
		System.out.println("--------------------------------------");
		System.out.println("Calling doMerge()");
		manager.merge(cust);
		System.out.println("Returning from doMerge()");
	}
 
	public void doFlush(int pKey) {
		System.out.println("--------------------------------------");
		System.out.println("Calling doFlush()");
		System.out.println("manager.find()");
		Customer cust = manager.find(Customer.class, pKey);
		System.out.println("cust.setName()");
		cust.setFirstName("doFlush");
		System.out.println("calling manager.flush()");
		manager.flush();
		System.out.println("returning from doFlush()");
	}
 
	public void doRemove(int pKey) {
		System.out.println("--------------------------------------");
		System.out.println("Calling doRemove()");
		System.out.println("manager.find()");
		Customer cust = manager.find(Customer.class, pKey);
		System.out.println("calling manager.remove()");
		manager.remove(cust);
		System.out.println("returning from doRemove()");
	}
}

Customer.java

package com.titan.domain;
 
import javax.persistence.*;
 
@Entity
@EntityListeners(com.titan.stats.EntityListener.class)
public class Customer implements java.io.Serializable {
	private int id;
 
	private String lastName;
 
	private String firstName;
 
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
 
	public void setId(int pk) {
		id = pk;
	}
 
	public String getLastName() {
		return lastName;
	}
 
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
 
	public String getFirstName() {
		return firstName;
	}
 
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
}

EntityStatsMBean.java

package com.titan.stats;
 
public interface EntityStatsMBean {
	public String output();
}

EntityStats.java

package com.titan.stats;
 
public class EntityStats implements EntityStatsMBean {
	public String output() {
		StringBuffer buf = new StringBuffer(
				"<table><tr><td>Entity Name</td>
                                          <td>Loads</td><td>Inserts</td>
                                          <td>Updates</td><td>Deletes</td></tr>");
		for (EntityListener.Stats stats : EntityListener.map.values()) {
			buf.append("<tr><td>" + stats.entity).append("</td>");
			buf.append("<td>" + stats.loads).append("</td>");
			buf.append("<td>" + stats.inserts).append("</td>");
			buf.append("<td>" + stats.updates).append("</td>");
			buf.append("<td>" + stats.removes).append("</td></tr>");
		}
		buf.append("</table>");
		return buf.toString();
	}
 
}

EntityListener.java

package com.titan.stats;
 
import javax.persistence.*;
import java.util.concurrent.ConcurrentHashMap;
 
public class EntityListener {
	public static class Stats {
		public String entity;
		public int updates;
		public int loads;
		public int inserts;
		public int removes;
	}
 
	public static ConcurrentHashMap<String, Stats> map = 
                                  new ConcurrentHashMap<String, Stats>();
 
	private static Stats getStats(Object entity) {
		String name = entity.getClass().getName();
		Stats stats = map.get(name);
		if (stats == null) {
			stats = new Stats();
			map.put(name, stats);
			stats.entity = name;
		}
		return stats;
	}
 
	@PostUpdate
	public void update(Object entity) {
		System.out.println("@PostUpdate: " + entity.getClass().getName());
		Stats stats = getStats(entity);
		synchronized (stats) {
			stats.updates++;
		}
	}
 
	@PostPersist
	public void persist(Object entity) {
		System.out.println("@PostPersist: " + entity.getClass().getName());
		Stats stats = getStats(entity);
		synchronized (stats) {
			stats.inserts++;
		}
	}
 
	@PostLoad
	public void load(Object entity) {
		System.out.println("@PostLoad: " + entity.getClass().getName());
		Stats stats = getStats(entity);
		synchronized (stats) {
			stats.loads++;
		}
	}
 
	@PostRemove
	public void remove(Object entity) {
		System.out.println("@PostRemove: " + entity.getClass().getName());
		Stats stats = getStats(entity);
		synchronized (stats) {
			stats.removes++;
		}
	}
}

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence:persistence version="1.0" 
xmlns:persistence="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 persistence_1_0.xsd ">
  <persistence:persistence-unit name="titan" transaction-type="JTA">
    <persistence:description>none</persistence:description>
    <persistence:jta-data-source>java:/TitanDB</persistence:jta-data-source>
    <persistence:properties>
    	<persistence:property name="hibernate.hbm2ddl.auto" value="create"/>
    </persistence:properties>
  </persistence:persistence-unit>
</persistence:persistence>

application.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
	"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
	"http://java.sun.com/dtd/application_1_3.dtd">
<application>
	<display-name>TitanCruisesExample</display-name>
	<module>
		<ejb>TravelAgentBean.jar</ejb>
	</module>
		<module>
		<ejb>EntityStats.jar</ejb>
	</module>
	<module>
		<ejb>DomainBean.jar</ejb>
	</module>
</application>

build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="Packaging Generator" default="_packaging_generation_">
	<target name="_packaging_generation_" depends="TitanCruisesExample" />
	<target name="TitanCruisesExample" description="TitanCruises.ear" 
	                  depends="TravelAgentBean">
		<jar destfile="TitanCruises.ear">
			<zipfileset dir="conf" prefix="META-INF">
				<include name="application.xml" />
			</zipfileset>
			<zipfileset dir="/home/ranjan/workspaceEJB3/TitanCruises10_2">
				<include name="TravelAgentBean.jar" />
				<include name="DomainBean.jar" />
				<include name="EntityStats.jar" />
			</zipfileset>
		</jar>
	</target>
	<target name="TravelAgentBean" description="DataAccessBean.jar" 
	                  depends="EntityStats">
		<jar destfile="TravelAgentBean.jar">
			<zipfileset 
			dir="/home/ranjan/workspaceEJB3/TitanCruises10_2/bin/com/titan/travelagent" prefix="com/titan/travelagent" />
		</jar>
	</target>
	<target name="EntityStats" description="EntityStats.jar" depends="DomainBean">
		<jar destfile="EntityStats.jar">
			<zipfileset 
			dir="/home/ranjan/workspaceEJB3/TitanCruises10_2/bin/com/titan/stats" 
			prefix="com/titan/stats" />
		</jar>
	</target>
	<target name="DomainBean" description="DomainBean.jar">
		<jar destfile="DomainBean.jar">
			<zipfileset dir="conf" prefix="META-INF">
				<include name="persistence.xml" />
			</zipfileset>
			<zipfileset 
			dir="/home/ranjan/workspaceEJB3/TitanCruises10_2/bin/com/titan/domain" prefix="com/titan/domain" />
		</jar>
	</target>
</project>

Data Source: postgres-TitanDB-ds.xml

This file is deployed in the deploy directory of the JBoss Application Server.

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>TitanDB</jndi-name>
	<use-java-context>true</use-java-context>
    <connection-url>jdbc:postgresql://127.0.0.1:5432/ejb3db</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>ranjan</user-name>
    <password>ranjan</password>
      <metadata>
         <type-mapping>PostgreSQL 8.0</type-mapping>
      </metadata>
  </local-tx-datasource>
</datasources>

Packaging Structure

TitanCruises.ear
  |
  |--- META-INF
  |     |
  |     |--- application.xml
  |     |
  |     |--- MENIFEST.MF
  |
  |--- DomainBean.jar
  |     |
  |     |--- META-INF 
  |     |     |
  |     |     |--- persistence.xml
  |     |     |    
  |     |     |--- MENIFEST.MF
  |     |
  |     |--- com
  |           |
  |           |--- titan
  |                  |
  |                  |--- domain
  |                         |
  |                         |--- Customer.class
  |
  |--- DataAccessBean.jar
  |     |
  |     |--- META-INF 
  |     |     |
  |     |     |--- MENIFEST.MF
  |     |
  |     |--- com
  |            |
  |            |--- titan
  |                  |
  |                  |--- travelagent
  |                         |
  |                         |--- TravelAgentRemote.class
  |                         |--- TravelAgentBean.class
  |--- EntityStats.jar
  |     |
  |     |--- META-INF 
  |     |     |
  |     |     |--- MENIFEST.MF
  |     |
  |     |--- com
  |            |
  |            |--- titan
  |                  |
  |                  |--- stats
  |                         |
  |                         |--- EntityStatsMBean.class
  |                         |--- EntityStats.class
  |                         |--- EntityListener.class

Server Console after successful deployment of the application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
16:30:57,642 INFO  [JBossASKernel] Created KernelDeployment for: DomainBean.jar
16:30:57,656 INFO  [JBossASKernel] installing bean: persistence.units:ear=TitanCruises.ear,unitName=titan
16:30:57,656 INFO  [JBossASKernel]   with dependencies:
16:30:57,656 INFO  [JBossASKernel]   and demands:
16:30:57,656 INFO  [JBossASKernel]      jboss.jca:name=TitanDB,service=DataSourceBinding
16:30:57,656 INFO  [JBossASKernel]   and supplies:
16:30:57,657 INFO  [JBossASKernel]      persistence.units:unitName=titan
16:30:57,657 INFO  [JBossASKernel] Added bean(persistence.units:ear=TitanCruises.ear,unitName=titan) to KernelDeployment of: DomainBean.jar
16:30:57,770 INFO  [STDOUT] ======> Creating interceptor metadata bridge
16:31:26,503 INFO  [JBossASKernel] Created KernelDeployment for: TravelAgentBean.jar
16:31:26,503 INFO  [JBossASKernel] installing bean: jboss.j2ee:ear=TitanCruises.ear,jar=TravelAgentBean.jar,name=TravelAgentBean,service=EJB3
16:31:26,503 INFO  [JBossASKernel]   with dependencies:
16:31:26,503 INFO  [JBossASKernel]   and demands:
16:31:26,503 INFO  [JBossASKernel]      persistence.units:ear=TitanCruises.ear,unitName=titan
16:31:26,503 INFO  [JBossASKernel]      jboss.ejb:service=EJBTimerService
16:31:26,503 INFO  [JBossASKernel]   and supplies:
16:31:26,503 INFO  [JBossASKernel]      jndi:TitanCruises/TravelAgentBean/remote
16:31:26,503 INFO  [JBossASKernel]      jndi:TravelAgentBean
16:31:26,503 INFO  [JBossASKernel]      Class:com.titan.travelagent.TravelAgentRemote
16:31:26,503 INFO  [JBossASKernel]      jndi:TitanCruises/TravelAgentBean/remote-com.titan.travelagent.TravelAgentRemote
16:31:26,503 INFO  [JBossASKernel] Added bean(jboss.j2ee:ear=TitanCruises.ear,jar=TravelAgentBean.jar,name=TravelAgentBean,service=EJB3) to KernelDeployment of: TravelAgentBean.jar
16:31:26,533 INFO  [PersistenceUnitDeployment] Starting persistence unit persistence.units:ear=TitanCruises.ear,unitName=titan
16:31:26,650 INFO  [Version] Hibernate Annotations 3.4.0.CR1
16:31:26,726 INFO  [Environment] Hibernate 3.3.0.CR1
16:31:26,740 INFO  [Environment] hibernate.properties not found
16:31:26,748 INFO  [Environment] Bytecode provider name : javassist
16:31:26,762 INFO  [Environment] using JDK 1.4 java.sql.Timestamp handling
16:31:27,019 INFO  [Version] Hibernate Commons Annotations 3.1.0.CR1
16:31:27,026 INFO  [Version] Hibernate EntityManager 3.4.0.CR1
16:31:27,168 WARN  [Ejb3Configuration] Persistence provider caller does not implement the EJB3 spec correctly. PersistenceUnitInfo.getNewTempClassLoader() is null.
16:31:27,349 INFO  [AnnotationBinder] Binding entity from annotated class: com.titan.domain.Customer
16:31:27,431 INFO  [EntityBinder] Bind entity com.titan.domain.Customer on table Customer
16:31:27,539 INFO  [Version] Hibernate Validator 3.1.0.CR1
16:31:27,807 INFO  [ConnectionProviderFactory] Initializing connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
16:31:27,811 INFO  [InjectedDataSourceConnectionProvider] Using provided datasource
16:31:28,501 INFO  [SettingsFactory] RDBMS: PostgreSQL, version: 8.1.11
16:31:28,501 INFO  [SettingsFactory] JDBC driver: PostgreSQL Native Driver, version: PostgreSQL 8.1 JDBC3 with SSL (build 412)
16:31:28,588 INFO  [Dialect] Using dialect: org.hibernate.dialect.PostgreSQLDialect
16:31:28,605 INFO  [TransactionFactoryFactory] Transaction strategy: org.hibernate.ejb.transaction.JoinableCMTTransactionFactory
16:31:28,637 INFO  [TransactionManagerLookupFactory] instantiating TransactionManagerLookup: org.hibernate.transaction.JBossTransactionManagerLookup
16:31:29,156 INFO  [TransactionManagerLookupFactory] instantiated TransactionManagerLookup
16:31:29,156 INFO  [SettingsFactory] Automatic flush during beforeCompletion(): disabled
16:31:29,157 INFO  [SettingsFactory] Automatic session close at end of transaction: disabled
16:31:29,157 INFO  [SettingsFactory] JDBC batch size: 15
16:31:29,157 INFO  [SettingsFactory] JDBC batch updates for versioned data: disabled
16:31:29,159 INFO  [SettingsFactory] Scrollable result sets: enabled
16:31:29,159 INFO  [SettingsFactory] JDBC3 getGeneratedKeys(): disabled
16:31:29,159 INFO  [SettingsFactory] Connection release mode: auto
16:31:29,162 INFO  [SettingsFactory] Default batch fetch size: 1
16:31:29,162 INFO  [SettingsFactory] Generate SQL with comments: disabled
16:31:29,162 INFO  [SettingsFactory] Order SQL updates by primary key: disabled
16:31:29,162 INFO  [SettingsFactory] Order SQL inserts for batching: disabled
16:31: