最近在看一本介绍ejb3的新书---《EJB3 in action》, 我觉得还写得可以,如果再结合网上黎活明写的开源文档---《JbossEJB3.0实例教程》一起学习,肯定会达到事半功倍的效果。(这两本书都可以在网上搜到,也可以上得益网下载)。
1.本案例相对简单,但基本用到了ejb3.0的一些基础知识,包括stateless session bean,entity bean,messageDriven bean,transaction,interceptor。
2.本案例以jboss为j2ee应用服务器,以Eclipse3.2作为开发的IDE,oracle10g为database,当然jdk应为1.5以上,因为java的metadata annotation(注释)是在jdk1.5才出来的。
3.本案例的设计思路:在实际应用中,例如一个web电子商务系统,用户在网上注册后,系统往往将用户的一些基本信息如姓名,年龄,email等发送给其它系统,如和它相关的一个邮递系统或广告宣传系统(仅仅是假设哈!)。那么就可以利用ejb3.0的messageDrivenBean来接收其它系统发送过来的message。本例先写一个entity bean----Customer.java,接着写一个statelessBean(MessageSender.java)来发送消息(即发送一个Customer实例),然后写一个messageDrivenBean(MessageReciverMDB.java)来接收消息,最后利用DI注入一个service类(CustomerService)来完成业务逻辑,将customer对象存入database里。
4.本案例的应用目录如下:
messageDrivenBean
src
com.wang.ejb3.dao
com.wang.ejb3.service
com.wang.entity
com.wang.mdb
com.wang.mdb.app
META-INF
lib
build.xml
5.各个包中的java类
(1)com.wang.entity
Customer.java
import java.util.Date;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Column;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = " customers " )
public class Customer implements Serializable{
public static final long serialVersionUID =- 1814239825517340645L ;
private Long id;
private String name;
private int age;
private Date birthday;
public Customer(){}
public Customer(String name, int age,Date birthday){
this .name = name;
this .age = age;
this .birthday = birthday;
}
public int getAge() {
return age;
}
public void setAge( int age) {
this .age = age;
}
@Temporal(value = TemporalType.TIMESTAMP)
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this .birthday = birthday;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this .id = id;
}
@Column(nullable = false ,length = 40 )
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
}
( 2) com.wang.ejb3.dao
CustomerDao.java
import com.wang.entity.Customer;
public interface CustomerDao{
void addCustomer(Customer customer) throws Exception;
void deleteCustomerById(Long id) throws Exception;
Customer getCustomerById(Long id) throws Exception;
}
CustomerDaoBean.java
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import com.wang.entity.Customer;
@Stateless
@Remote(CustomerDao. class )
public class CustomerDaoBean implements CustomerDao{
@PersistenceContext
protected EntityManager entityManager;
public void addCustomer(Customer customer) throws Exception{
entityManager.persist(customer);
}
public void deleteCustomerById(Long id) throws Exception{
entityManager.remove(getCustomerById(id));
}
public Customer getCustomerById(Long id) throws Exception{
return (Customer)entityManager.find(Customer. class , id);
}
}
(3)com.wang.ejb3.service
CustomerService.java
import com.wang.entity.Customer;
public interface CustomerService{
void addCustomer(Customer customer);
void deleteCustomerById(Long id);
Customer getCustomerById(Long id);
}
CustomerServiceBean.java
import javax.ejb.Remote;
import javax.ejb.EJB;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.SessionContext;
import com.wang.ejb3.dao.CustomerDao;
import com.wang.entity.Customer;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionManagementType;
import javax.ejb.TransactionAttributeType;
@Stateless
@Remote(CustomerService. class )
@TransactionManagement(TransactionManagementType.CONTAINER)
public class CustomerServiceBean implements CustomerService{
@Resource
private SessionContext context;
@EJB(beanName = " CustomerDaoBean " )
protected CustomerDao customerDao;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void addCustomer(Customer customer){
try {
customerDao.addCustomer(customer);
} catch (Exception e){
e.printStackTrace();
context.setRollbackOnly();
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteCustomerById(Long id){
try {
customerDao.deleteCustomerById(id);
} catch (Exception e){
e.printStackTrace();
context.setRollbackOnly();
}
}
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public Customer getCustomerById(Long id){
try {
return customerDao.getCustomerById(id);
} catch (Exception e){
e.printStackTrace();
context.setRollbackOnly();
return null ;
}
}
}
以上为经典的分层设计模式,包括persistence object,data access object,business service object(完整的应用还应该有controller object和view层)。因为本案例只是简单的实现messageDrivenBean的功能,所以就没有涉及web层(controller和view),只用一个java类的main方法来发送message。
下面来看看本案例的两个核心类,statelessBean(MessageSender.java)来发送消息(即发送一个Customer实例),然后写一个messageDrivenBean(MessageReciverMDB.java)来接收消息。它们为于com.wang.mdb包中
(4)com.wang.mdb
1. MSender.java
import javax.ejb.Remote;
import com.wang.entity.Customer;
@Remote
public interface MSender{
void sendMessage(Customer sender);
}
2.MessageSender.java
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.Session;
import javax.jms.ObjectMessage;
import javax.jms.MessageProducer;
import javax.ejb.Stateless;
import javax.annotation.Resource;
import javax.interceptor.Interceptors;
import com.wang.entity.Customer;
@Stateless
public class MessageSender implements MSender{
@Resource(mappedName = " QueueConnectionFactory " )
private ConnectionFactory connectionFactory;
@Resource(mappedName = " queue/wang " )
private Destination destination;
@Interceptors(MessageInterceptorLog. class )
public void sendMessage(Customer sender){
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession( true , Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
ObjectMessage message = session.createObjectMessage();
message.setObject(sender);
producer.send(message);
producer.close();
session.close();
connection.close();
System.out.print( " message send success " );
} catch (Exception e){
e.printStackTrace();
}
}
}
3.MessageReciverMDB.java
import javax.ejb.MessageDriven;
import javax.ejb.ActivationConfigProperty;
import javax.jms.Message;
import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.interceptor.Interceptors;
import javax.ejb.EJB;
import com.wang.ejb3.service.CustomerService;
import com.wang.entity.Customer;
@MessageDriven(
mappedName = " MessageReciverMDB " ,
messageListenerInterface = javax.jms.MessageListener. class ,
activationConfig = {
@ActivationConfigProperty(propertyName = " destinationType " ,propertyValue = " javax.jms.Queue " ),
@ActivationConfigProperty(propertyName = " destination " ,propertyValue = " queue/wang " ),
@ActivationConfigProperty(propertyName = " acknowledgeMode " ,propertyValue = " DUPS_OK_ACKNOWLEDGE " )
}
)
public class MessageReciverMDB implements MessageListener{
@EJB(beanName = " CustomerServiceBean " )
protected CustomerService customerSercvice;
private Customer sender;
@Interceptors(MessageInterceptorLog. class )
public void onMessage(Message message){
try {
ObjectMessage objectMessage = (ObjectMessage)message;
sender = (Customer)objectMessage.getObject();
System.out.println(sender.getName());
System.out.println(sender.getAge());
System.out.println(sender.getBirthday());
this .customerSercvice.addCustomer(sender);
} catch (JMSException jme){
jme.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
}
}
以上为本例的核心部分,其中annotation的一些配置和ejb3 in action上不一样,因为是运行在jboss上,所以要以jboss的配置为准。如@ActivationConfigPropert(propertyName="destination",propertyValue="queue/wang"),如果你将destination写成distinationName的话会报错,在http://www.manning-sandbox.com/thread.jspa?threadID=20021&tstart=0 上你可以看到关于这个配置的错误:RequiredConfigPropertyMetaData@201d6d[name=destination descriptions=[DescriptionMetaData@5d8e63[language=de]]] for messagingType 'javax.jms.MessageListener' not found in activation config [ActivationConfigProperty(destinationType=javax.jms.Queue), ActivationConfigProperty(destinationName=queue/OrderProcessorQueue)] ra=jboss.jca:service=RARDeployment,name='jms-ra.rar'
有人回复说:You are not the only one having this problem. We did the same fix for our JBoss sample code. This appears to be a JBoss bug...
但是这不是jboss的bug,而是由于参数名的不同所引起的,所以大家关于annotation的参数配置一定要依据不同厂商的服务器的来确定,有可能不同的服务器有它自己特殊的要求,这也是用annotation来封装配置信息的一个不足之处,封装的越多,程序的实现越简单,但程序员知道的也就越少,对错误的查找和代码的测试更加困难,有点失去对程序的主控权,写程序越来越像敲字母,不得不依赖于厂商的解决(比如bug)。
4.MessageInterceptorLog.java(一个简单的AOP)
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.sql.Timestamp;
import com.wang.entity.Customer;
public class MessageInterceptorLog{
@AroundInvoke
public Object logMethodEntry(InvocationContext context) throws Exception{
System.out.println( new Timestamp(System.currentTimeMillis()) + " Enter method: " + context.getMethod().getName());
Object[] args = context.getParameters();
for ( int i = 0 ;i < args.length;i ++ ){
if (args[i] instanceof Customer){
Customer customer = (Customer)args[i];
if (customer.getName().equals( " wang " )){
customer.setName( " wangwang " );
}
}
}
Object returnValue = context.proceed();
System.out.println( new Timestamp(System.currentTimeMillis()) + " Out method: " + context.getMethod().getName());
return returnValue;
}
}
(5)com.wang.mdb.app
MessageSenderApp.java(用一个java类来调用消息发送类)
import com.wang.mdb. * ;
import java.util. * ;
import javax.naming.InitialContext;
import com.wang.entity.Customer;
public class MessageSenderApp{
public static void main(String[] args){
Properties props = new Properties();
props.setProperty( " java.naming.factory.initial " ,
" org.jnp.interfaces.NamingContextFactory " );
props.setProperty( " java.naming.provider.url " , " localhost:1099 " );
props.setProperty( " java.naming.factory.url.pkgs " ,
" org.jboss.naming:org.jnp.interfaces " );
try {
InitialContext ctx = new InitialContext(props);
MSender sender = (MSender)ctx.lookup( " MessageSender/remote " );
Customer sender1 = new Customer( " wangwui " , 24 , new Date());
sender.sendMessage(sender1);
} catch (Exception e){
e.printStackTrace();
}
}
}
jboss下 oracle数据源的配置:
1.写一个oracle-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>OracleDS</jndi-name>
<connection-url>jdbc:oracle:thin:@wang(你的计算机名):1521:orcl</connection-url>
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<user-name>system</user-name>
<password>d123456</password>
<metadata>
<type-mapping>Oracle10g</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
将它放到你jboss中的部署目录下 例如:F:/j2EE/jboss-4.2.0.GA/server/default/deploy下
将oracle的驱动jar包-----ojdbc14.jar(在你安装的oracle目录下去找或者网上下www.oracle.com)复制到jboss的default的lib里 例如:F:/j2EE/jboss-4.2.0.GA/server/default/lib下
java持久化单元的配置:
在META-INF中写一个persistence.xml
<?xml version="1.0"?>
<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_1_0.xsd" version="1.0">
<persistence-unit name="person">
<jta-data-source>java:/OracleDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>(自动创建数据库)
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
</properties>
</persistence-unit>
</persistence>
最后application用ant工具编译,打包成jar文件,部署到jboss中
< property environment ="env" />
< property name ="app.dir" value ="${basedir}" />
< property name ="src.dir" value ="${app.dir}/src" />
< property name ="jboss.home" value ="${env.JBOSS_HOME}" />
< property name ="jboss.server.config" value ="default" />
< property name ="build.dir" value ="${app.dir}/build" />
< property name ="build.classes.dir" value ="${build.dir}/classes" />
<!-- Build classpath -->
< path id ="build.classpath" >
< fileset dir ="${basedir}/lib" >
< include name ="*.jar" />
</ fileset >
< pathelement location ="${build.classes.dir}" />
</ path >
<!-- =================================================================== -->
<!-- Prepares the build directory -->
<!-- =================================================================== -->
< target name ="prepare" depends ="clean" >
< mkdir dir ="${build.dir}" />
< mkdir dir ="${build.classes.dir}" />
</ target >
<!-- =================================================================== -->
<!-- Compiles the source code -->
<!-- =================================================================== -->
< target name ="compile" depends ="prepare" description ="编绎" >
< javac srcdir ="${src.dir}" destdir ="${build.classes.dir}" debug ="on" deprecation ="on" optimize ="off" includes ="**" >
< classpath refid ="build.classpath" />
< exclude name ="junit/**/*.*" />
</ javac >
</ target >
< target name ="ejbjar" depends ="compile" description ="创建EJB发布包" >
< jar jarfile ="${app.dir}/MDBean.jar" >
< fileset dir ="${build.classes.dir}" >
< include name ="com/**/*.class" />
</ fileset >
< metainf dir ="${src.dir}/META-INF" >
< include name ="**" />
</ metainf >
</ jar >
</ target >
< target name ="deploy" depends ="ejbjar" >
< copy file ="${app.dir}/EntityBean.jar" todir ="${jboss.home}/server/${jboss.server.config}/deploy" />
</ target >
<!-- =================================================================== -->
<!-- Cleans up generated stuff -->
<!-- =================================================================== -->
< target name ="clean" >
< delete dir ="${build.dir}" />
< delete file ="${jboss.home}/server/${jboss.server.config}/deploy/MDBean.jar" />
</ target >
</ project >
最后运行MessageSenderApp.java