花了几天的时间研究了一下EJB的使用,一直以来都主要是在写终端中的程序,对Java框架的相关的开发很不熟悉,中间遇到了不少麻烦,还好总算都解决了。写篇日志记录一下。
从8开始最新的jboss已经改名为WildFly了,jboss5还是09年的东西.刚开始的时候我准备用WildFly部署EJB的,因为没有经验而且相关的资料很少.只能换用更老一点的Jboss5
MyEclipse可保存后就自动发布,Eclipse-J2EE版似乎没有这个功能,不过即使是MyEclipse的自动发布也可能存在问题,比较稳妥的办法是自己写一个ant的自动编译文件.
ant配置文件的写法
ant可以帮助完成编译,复制,粘贴,打包,部署等重复的操作,下面是一份ant的模版.
<?xml version="1.0" encoding="UTF-8"?>
<project name="EJB" basedir=".">
<property name="src.dir" value="${basedir}/ejbModule" />
<property name="jboss.home" value="/home/ckboss/jboss-5.1.0.GA/" />
<property name="jboss.server.config" value="default" />
<property name="build.dir" value="${basedir}/build" />
<path id="build.classpath">
<fileset dir = "${jboss.home}/client">
<include name="*.jar" />
</fileset>
<pathelement location ="${build.dir}" />
</path>
<target name="prepare">
<delete dir="${build.dir}" />
<mkdir dir="${build.dir}" />
</target>
<target name="compile" depends="prepare" description="编译">
<javac srcdir="${src.dir}" destdir="${build.dir}">
<classpath refid = "build.classpath" />
</javac>
</target>
<target name = "ejbjar" depends="compile" description="创建EJB">
<jar jarfile="${build.dir}/${ant.project.name}.jar">
<fileset dir="${build.dir}">
<include name="**/*.class" />
</fileset>
</jar>
</target>
<target name="deploy" depends="ejbjar" description="发布">
<copy file ="${build.dir}/${ant.project.name}.jar" todir = "${jboss.home}/server/${jboss.server.config}/deploy/" />
</target>
<target name="undeploy" description="卸载">
<delete file="${jboss.home}/server/${jboss.server.config}/deploy/${ant.project.name}.jar" />
</target>
</project>
详细解释一下ant的使用:
1.在工程目录下新建一个叫 build.xml 的文件填上以上内容就可以了,然后会发现xml变成了小蚂蚁的图标,这样就可以了.这个build.xml所在的位置就叫做basedir , 其他文件可以根据这个相对路径来定位.
2.代码的解释:
<project name="EJB" basedir=".">
<property name="src.dir" value="${basedir}/ejbModule" />
<property name="jboss.home" value="/home/ckboss/jboss-5.1.0.GA/" />
<property name="jboss.server.config" value="default" />
<property name="build.dir" value="${basedir}/build" />
这一段主要定义了一些变量的路径,project name 就是工程的名字,也就是${ant.project.name}
src.dir:是源码位置
jboss.home: jboss部署在哪
jboss.server.config: jboss的启动配置
build.dir: 生成的jar包的位置
<path id="build.classpath">
<fileset dir = "${jboss.home}/client">
<include name="*.jar" />
</fileset>
<pathelement location ="${build.dir}" />
</path>
所需要的jar包的位置,这里包含了jboss下client的所有jar包,如果引入了别的jar包可以手动的包含进去.
<target name="prepare">
<delete dir="${build.dir}" />
<mkdir dir="${build.dir}" />
</target>
这里定义了一个叫prepare的过程,功能是删除可能存在的build.dir,再新建一个build.dir
<target name="compile" depends="prepare" description="编译">
<javac srcdir="${src.dir}" destdir="${build.dir}">
<classpath refid = "build.classpath" />
</javac>
</target>
一个叫做 compile 的过程,这个过程的执行需要prepare在它前面执行.
编译所有src里的文件,编译到build里
<target name = "ejbjar" depends="compile" description="创建EJB">
<jar jarfile="${build.dir}/${ant.project.name}.jar">
<fileset dir="${build.dir}">
<include name="**/*.class" />
</fileset>
</jar>
</target>
创建一个jar包到build.dir中,包含了build.dir里的class类
<target name="deploy" depends="ejbjar" description="发布">
<copy file ="${build.dir}/${ant.project.name}.jar"
todir = "${jboss.home}/server/${jboss.server.config}/deploy/" />
</target>
<target name="undeploy" description="卸载">
<delete file="${jboss.home}/server/${jboss.server.config}/deploy/${ant.project.name}.jar" />
</target>
发布和卸载,在jboss上的部署非常简单,发布只要把jar拷贝到jboss里对应的deploy目录里就可以了,卸载就是把这个jar包移除就可以了.
3.如何使用ant?
点击XML文件,选择大纲视图,选择所需的步骤,右键用ant运行
这样的界面就是运行成功了
jndi比较难以理解,各种服务器的调用也个不一样,放弃WildFly的一个原因就是不知道怎么用jndi,也找不到靠普的文档
但jboss5中的jndi使用比较简单,
控制台上的输出就是某个应用的jndi
如何获取jndi,
外部调用ejb就要用Context找到相应的jndi,并转换成相应的接口,获取Contex的参数设置各种服务器是不一样的,jboss5中的一种调用方法是:
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming rg.jnp.interfaces");
properties.setProperty(Context.PROVIDER_URL, "localhost:1099");
InitialContext ctx = new InitialContext(properties);
为了写起来更方便,也可以直接在src下建立一个叫jndi.properties的配置文件,这样就不用写一堆properties了
这个配置文件内容如下:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming rg.jnp.interfaces
java.naming.provider.url=localhost\:1099
有了这个配置文件只要写一句就可以了
InitialContext ctx = new InitialContext();
有了ctx后就可以将对应的jndi找到并转换成接口了
如下,调用一个远程接口
AccountServerRemote asl = (AccountServerRemote) ctx.lookup("AccountServer/remote");
用实体bean来完成对数据库的持久化操作.因为以前用过hibernate在处理数据库的时候就自然想到了用hibernate.于是想法设法把hibernate往EJB里弄,忙活了一下午,最后无果而终,看了JPA之后才知道JPA采用的就是hibernate的实现…..
ejb使用数据库第一步就是连接写一个*-ds.xml文件,这个可以在jboss目录下 ==jboss-5.1.0.GA/docs/examples/jca/== 找到,下面是一个msql-ds.xml的文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- See http://www.jboss.org/community/wiki/Multiple1PC for information about local-tx-datasource -->
<!-- $Id: mysql-ds.xml 88948 2009-05-15 14:09:08Z jesper.pedersen $ -->
<!-- Datasource config for MySQL using 3.0.9 available from: http://www.mysql.com/downloads/api-jdbc-stable.html -->
<datasources>
<local-tx-datasource>
<jndi-name>tomysql</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/USERS</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>javaTest</user-name>
<password>123456</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<!-- should only be used on drivers after 3.22.1 with "ping" support <valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name> -->
<!-- sql to call when connection is created <new-connection-sql>some arbitrary sql</new-connection-sql> -->
<!-- sql to call on an existing pooled connection when it is obtained from pool - MySQLValidConnectionChecker is preferred for newer drivers <check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql> -->
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
改成自己的内容后,粘贴到==jboss-5.1.0.GA/server/default/deploy/==,
同时要把相应的驱动放到==/jboss-5.1.0.GA/server/default/lib/==
同时在项目目录META-INF下要新建一个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_1_0.xsd" version="1.0">
<persistence-unit name="ejbdb" transaction-type="JTA">
<jta-data-source>java:tomysql</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
可以看到,这里直接使用hibernate的配置就可以了…..
注意一定要保证打包后persistence.xml在META-INF中….
ejb里实体bean开发是使用注解的,基本上和hibernate一样,但是用一个叫 @PersistenceContext private EntityManager em;的东西进行操作(就像hibernate里的session),用类似与HQL用一种叫JPQL的东西查询…..
首先需要新建一个叫*-service.xml的文件新建一个队列,
文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=myMDB">
<attribute name="JNDIName">queue/myMDB</attribute>
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>
</server>
新建一个叫做queue/myMDB的jndi
发送一个消息:
InitialContext ctx = new InitialContext();
QueueConnectionFactory factory = (QueueConnectionFactory)ctx.lookup("ConnectionFactory");
QueueConnection conn = factory.createQueueConnection();
QueueSession session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
Destination destination = (Destination) ctx.lookup("queue/myMDB");
MessageProducer producer = session.createProducer(destination);
producer.send(session.createTextMessage(MSG));
session.close();
conn.close();
接受一个消息:
@MessageDriven
(
mappedName = "jms/MessageDriverBean",
activationConfig =
{
@ActivationConfigProperty
(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty
(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty
(propertyName = "destination" , propertyValue = "queue/myMDB")
}
)
//重载的onmessage方法
@Override
public void onMessage(Message msg) {
TextMessage tmsg = (TextMessage)msg;
try {
System.out.println(tmsg.getText());
FileWriter fw = new FileWriter("/home/ckboss/桌面/Log.record", true);
fw.append(tmsg.getText());
fw.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我也不知道EJB的正确姿势是什么样的…
发布一个EJB后,其他的java项目可以通过Jndi找到对应的bean,但是需要在源码中包含这个bean的接口.
为了方便,下面的登陆应用放在了一个EJB中.
包含了一个实体bean和一个点对点的消息驱动bean,由client包调用,如果登陆成功会通过消息驱动bean发送一条消息,并被接受到记录一条记录
文件结构
➜ EJB_LogIn tree
└── ejbModule
├── client
│ ├── client1.java
│ └── LogInClient.java
├── db
│ ├── Account.java
│ ├── AccountServer.java
│ ├── AccountServerLocal.java
│ └── AccountServerRemote.java
├── jndi.properties
├── META-INF
│ ├── MANIFEST.MF
│ └── persistence.xml
└── msg
├── MessageDriverBean.java
└── QueueSender.java
14 directories, 32 files
EJB_LogIn.zip下载1
EJB_LogIn.zip下载2