ClassLoader in Tomcat (http://rosonsandy.blogdriver.com/rosonsandy/871539.html)
1 - Tomcat
的类载入器的结构
Tomcat Server
在启动的时候将构造一个ClassLoader树,以保证模块的类库是私有的
Tomcat Server的ClassLoader结构如下:
+-----------------------------+
| Bootstrap |
| | |
| System |
| | |
| Common |
| / \ |
| Catalina Shared |
| / \ |
| WebApp1 WebApp2 |
+-----------------------------+
其中:
- Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
- System - 载入$CLASSPATH/*.class
- Common - 载入$CATALINA_HOME/common/...,它们对TOMCAT和所有的WEB APP都可见
- Catalina - 载入$CATALINA_HOME/server/...,它们仅对TOMCAT可见,对所有的WEB APP都不可见
- Shared - 载入$CATALINA_HOME/shared/...,它们仅对所有WEB APP可见,对TOMCAT不可见(也不必见)
- WebApp - 载入ContextBase?/WEB-INF/...,它们仅对该WEB APP可见
2 - ClassLoader
的工作原理
每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类
系统默认的contextClassLoader是systemClassLoader,所以一般而言java程序在执行时可以使用JVM自带的类、$JAVA_HOME/jre/lib/ext/中的类和$CLASSPATH/中的类
可以使用
Thread.currentThread().setContextClassLoader(...);
更改当前线程的contextClassLoader,来改变其载入类的行为
ClassLoader
被组织成树形,一般的工作原理是:
1) 线程需要用到某个类,于是contextClassLoader被请求来载入该类
2) contextClassLoader请求它的父ClassLoader来完成该载入请求
3) 如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入
注意
:WebApp?ClassLoader的工作原理和上述有少许不同:
它先试图自己载入类(在ContextBase?/WEB-INF/...中载入类),如果无法载入,再请求父ClassLoader完成
由此可得:
- 对于WEB APP线程,它的contextClassLoader是WebApp?ClassLoader
- 对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader
3 类的查找
ClassLoader类中loadClass方法为缺省实现,用下面的顺序查找类:
1、调用findLoadedClass方法来检查是否已经被加载。如果没有则继续下面的步骤。
2、如果当前类装载器有一个指定的委托父装载器,则用委托父装载器的loadClass方法加载类,也就是委托给父装载器加载相应的类。
3、如果这个类装载器的委托层级体系没有一个类装载器加载该类,则使用类装载器定位类的特定实现机制,调用findClass方法来查找类。
4 -
部分原代码分析
4.1 - org/apache/catalina/startup/Bootstrap.java
Bootstrap中定义了三个classloader:commonLoader,catalinaLoader,sharedLoader.三者关系如下:
//
注意三个自己定置的ClassLoader的层次关系:
// systemClassLoader (root)
// +--- commonLoader
// +--- catalinaLoader
// +--- sharedLoader
Tomcat Server
线程的起点
构造ClassLoader树,通过Thread.currentThread().setContextClassLoader(catalinaLoader)设置当前的classloader为catalinaLoader。
载入若干类,然后转入org.apache.catalina.startup.Catalina类中
4.2 org.apache.catalina.loader.StandardClassLoader.java
通过看loadClass这个方法来看tomcat是如何加载类的,顺序如下:
(0) Check our previously loaded class cache查找已经装载的class
clazz = findLoadedClass(name);
(1) If a system class, use system class loader通过系统classloader来装载class
ClassLoader loader = system;
clazz = loader.loadClass(name);
(2) Delegate to our parent if requested如果有代理则使用父类classloader
ClassLoader loader = parent;
if (loader == null)
loader = system;
clazz = loader.loadClass(name);
(3) Search local repositories 查找本地类池,比如$CATALINA_HOME/server
clazz = findClass(name);
(4) Delegate to parent unconditionally 默认使用代理装载器
[
查看代码]
4.3 - org/apache/catalina/startup/ClassLoaderFactory.java
根据设置创建并返回StandardClassLoader的实例
[
查看代码]
4.4 - org/apache/catalina/loader/StandardClassLoader.java
类载入器
4.5 - org/apache/catalina/startup/SecurityClassLoad.java
该类仅包含一个静态方法,用来为catalinaLoader载入一些类
[
查看代码]
Appendix -
参考
[1] http://jakarta.apache.org/tomcat/
中的Tomcat 4.1.x文档Class Loader HOW-TO
在一个
JVM
中可能存在多个
ClassLoader
,每个
ClassLoader
拥有自己的
NameSpace
。一个
ClassLoader
只能拥有一个
class
对象类型的实例,但是不同的
ClassLoader
可能拥有相同的
class
对象实例,这时可能产生致命的问题。如
ClassLoaderA
,装载了类
A
的类型实例
A1
,而
ClassLoaderB
,也装载了类
A
的对象实例
A2
。逻辑上讲
A1=A2
,但是由于
A1
和
A2
来自于不同的
ClassLoader
,它们实际上是完全不同的,如果
A
中定义了一个静态变量
c
,则
c
在不同的
ClassLoader
中的值是不同的。
posted @
2006-04-18 08:48 小小程序程序员混口饭吃 阅读(443) |
评论 (0) |
编辑 收藏
编程
我们将首先编写远程对象,并将代码保存为名字为AddServer.Java的文件:
import Java.rmi.*;
public interface AddServer extends Remote {
public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException;
}
|
我们来看看上面的代码。首先,为了使用其内容,我们导入rmi包。然后,我们创建一个扩展了Java.rmi中远程接口的接口。所有的远程对象必须扩展该远程接口,我们将该远程接口称为AddServer。在该远程对象中,有一个名字为AddNumbers的方法,客户端可以调用这一方法。我们必须记住的是,所有的远程方法都需要启动RemoteException方法,有错误发生时就会调用该方法。
下面我们开始编写远程对象的实现。这是一个实现远程对象并包含有所有方法代码的类,将下面的代码保存为名字为AddServerImpl.Java的文件:
import Java.rmi.*;
public class AddServerImpl extends UnicastRemoteObject implements AddServer { public AddServerImpl() { super(); } public int AddNumbers(int firstnumber,int secondnumber) throws RemoteException { return firstnumber + secondnumber; } } |
首先,我们导入rmi包,然后创建一个扩展UnicastRemoteObject和实现创建的远程对象的类;其次,我们可以为类创建一个缺省的构建器。我们还了解了AddNumbers方法的代码,它启动RemoteException。这样我们就覆盖了创建的远程对象中的方法。AddNumbers方法的代码非常好理解,它接受2个整型参数,然后相加并返回它们的和。
至此,我们已经有了二个Java文件:远程对象和远程对象的实现。下面我们将使用Javac命令编译这二个文件:
编译远程对象:
C:\jdk\bin\Javac workingdir\AddServer.Java |
编译远程对象实现:
C:\jdk\bin\Javac workingdir\AddServerImpl.Java |
这样,就会达到二个Java文件和二个类文件,下面我们将创建stub和skeleton。为了创建stub和skeleton文件,我们必须使用rmic编译器编译远程对象实现文件。
用Rmic编译远程对象实现文件:
C:\jdk\bin\rmic workingdir\AddServerImpl.Java |
然后,我们就会发现多了2个新建的类文件,它们分别是AddServerImpl_Stub.class 和AddServerImpl_Skel.class 。
The Coding (Contd.)
我们已经编译了所有的源代码,下面我们来创建客户端和服务器端,将下面的代码保存为名字为RmiServer.Java的文件:
import Java.rmi.*; import Java.net.*;
public class RmiServer { public static void main (String args[]) throws RemoteException, MalformedURLException { AddServerImpl add = new AddServerImpl(); Naming.rebind("addnumbers",add); } }
|
首先,我们导入Java.rmi包和Java.net包。另外,我们还使用throws从句捕获任何异常。我们从对象中得出远程对象实现,使用rebind方法将字符串addnumbers与该对象绑定。下面的例子显示了绑定的含义:
从现在开始,无论何时客户端要调用远程对象,使用字符串addnumbers就可以实现。rebind方法有二个参数:第一个参数是字符串变量,第二个参数是远程对象实现类的对象。
下面我们来创建客户端,将下面的代码保存为名字为RmiClient.Java的文件:
import Java.rmi.*; import Java.net.*;
public class RmiClient { public static void main(String args[]) throws RemoteException, MalformedURLException { String url="rmi://127.0.0.1/addnumbers"; AddServer add; add = (AddServer)Naming.lookup(url); int result = add.AddNumbers(10,5); System.out.println(result); } }
|
首先,我们导入Java.rmi包和Java.net包,并使用throws从句捕获所有必要的异常。然后通过利用Naming类中的静态lookup方法从远程对象中得到一个对象。(这也是我们无需从Naming类中得到一个对象并调用它。而只使用类名字的原因。)
lookup方法接受远程对象的完整的URL名字,该URL由完整的机器IP地址以及与对象绑定的字符串(也誻对象的绑定名)组成。在调用远程对象时,我们使用了RMI协议。lookup方法向我们返回一个对象,在能够使用它前,我们必须将它的数据类型转换为与远程对象的数据类型一致。
Since we have both our server and client source ready, let's compile them both: |
至此,我们已经有了服务器端和客户端的源代码,下面我们来编译这二个源文件:
编译远程服务器:
C:\jdk\bin\Javac workingdir\RmiServer.Java |
编译远程客户端:
C:\jdk\bin\Javac workingdir\RmiClient.Java |
在对我们的代码进行测试前,还必须首先启动RMI Registry。RMI Registry存储有所有绑定的数据,没有它,RMI就不能正常地运行!
启动Rmi Registry服务器:
C:\jdk\bin\start rmiregistry |
我们会注意到,这时会出现一个空白的DOS提示符窗口,这表明Rmi Registry服务器在运行,注意不要关闭该窗口。然后,我们首先在一个DOS提示符窗口中运行Rmi服务器,然后在另一个DOS提示符窗口中运行Rmi客户端。
启动RMI服务器:
C:\jdk\bin\Java workingdir\RmiServer |
启动RMI客户端:
C:\jdk\bin\Java workingdir\RmiClient |
如果一切正常,我们应该能够得到15这个输出。我们向AddNumbers方法输入10和5二个数字,该方法将这二者加起来,并将其和15返回给我们。如果得到了15这个输出,说明我们已经成功地执行了一个远程方法。当然,在这里,我们并没有执行真正意义上的远程方法,因为我们的计算机既是服务器,又是客户机。如果有计算机网络,我们就可以方便地进行执行远程方法的试验了。
posted @
2006-04-14 09:17 小小程序程序员混口饭吃 阅读(1805) |
评论 (0) |
编辑 收藏
Spring中事务的定义:
一、Propagation :
对于特定的方法或方法命名模式,代理的具体事务行为由事务属性驱动,如下面的例子所示:
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:
- PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
前六个策略类似于EJB CMT:常量名相同,因此,对EJB开发人员来说,应该立刻就感到熟悉。第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager),或者通过JTA支持嵌套事务。
二、Isolation Level(事务隔离等级):
1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
4、Read Uncommitted:保证了读取过程中不会读取到非法数据。
spring中的Isolation属性:
1、ISOLATION_DEFAULT :使用当前数据源的默认级别
2、ISOLATION_READ_UNCOMMITTED :Dirty reads, non-repeatable reads, and phantom reads can occur.
3、ISOLATION_READ_COMMITTED :Dirty reads are prevented; non-repeatable reads and phantom reads can occur.
4、ISOLATION_REPEATABLE_READ:Dirty reads and non-repeatable reads are prevented; phantom reads can occur.
5、ISOLATION_SERIALIZABLE:Dirty reads, non-repeatable reads, and phantom reads are prevented.
三、readOnly
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
四、Timeout
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
事务划分策略
1、推荐在业务层使用事务,这样可以允许业务层捕获导致rollback的异常,并抛出恰当的业务层异常;不在dao层使用事务是因为这会限制了dao重用其他事务需求,并且dao层没有实现业务逻辑,并且原子性也是业务层的概念。
spring声明性事务的划分:
1、有四个地方需要配置:The four participants are transaction manager, proxy factory, transaction interceptor, and a set of transaction attributes.
2、使用ProxyFactoryBean/Transaction Interceptor(transactionInterceptor)配置spring事务
以下为配置实例:
<!-- The DBCP DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<!-- The DAO class -->
<bean id="dao"
class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- The transactionmanager to use for regular non JTA datasource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- TransactionInterceptor -->
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="transactionAttributeSource"> <value>org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS,readOnlyorg.springframework.prospring.ticket.service.BoxOffice.allocate*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<!-- Transactional proxy for the primary business object -->
<bean id="boxOffice" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="boxOfficeTarget"/> </property> <property name="proxyInterfaces"> <value>org.springframework.prospring.ticket.service.BoxOffice</value> </property> <property name="interceptorNames"> <value>transactionInterceptor</value> </property>
</bean>
<!-- Business Object -->
<bean id="boxOfficeTarget"
class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
<property name="boxOfficeDao">
<ref local="dao"/>
</property>
</bean>
3、使用TransactionProxyFactoryBean配置spring事务
以下为配置实例:
<!-- The DBCP DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<!-- The DAO class -->
<bean id="dao"
class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- The transactionmanager to use for regular non JTA datasource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- Transactional proxy and the primary business object -->
<bean id="boxOffice" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="target"> <bean class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
<property name="boxOfficeDao">
<ref local="dao"/>
</property>
</bean>
</property>
<property name="transactionAttributes"> <props> <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="allocate*">PROPAGATION_REQUIRED</prop> </props> </property>
</bean>
4、使用BeanNameAutoProxyCreator配置spring事务
如果有大量的bean需要使用事物,那么只要在配置文件中提供bean name给BeanNameAutoProxyCreator,spring就会个给该bean提供事务代理,配置实例如下:
<!-- The DBCP DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<!-- The DAO class -->
<bean id="dao"
class="org.springframework.prospring.ticket.dao.jdbc.JdbcBoxOfficeDao">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- The transactionmanager to use for regular non JTA datasource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- TransactionInterceptor -->
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="transactionAttributeSource"> <value>org.springframework.prospring.ticket.service.BoxOffice.get*=PROPAGATION_SUPPORTS,readOnlyorg.springframework.prospring.ticket.service.BoxOffice.allocate*=PROPAGATION_REQUIRED </value> </property>
</bean>
<!-- BeanNameAutoProxyCreator -->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <value>transactionInterceptor</value> </property> <property name="beanNames"> <list> <idref local="boxOffice"/> </list> </property>
</bean>
<!-- Business Object -->
<bean id="boxOffice"
class="org.springframework.prospring.ticket.service.BoxOfficeImpl">
<property name="boxOfficeDao">
<ref local="dao"/>
</property>
</bean>
posted on 2006-04-04 09:06
loocky 阅读(0)
posted @
2006-04-13 09:04 小小程序程序员混口饭吃 阅读(432) |
评论 (0) |
编辑 收藏
Kodo是BEA公司收购SolarMetric公司后获得的持久层框架项目,以前只是支持JDO标准,2006年2月13日,BEA公司宣布发布Kodo项目的最新版本——Kodo 4.0.0 Early Access 4,Kodo 4.0.0 EA4支持EJB3和JDO2两个标准,本文中,我们将首先学习和了解KODO EJB,了解如何使用Kodo EJB完成开发工作。
我们将按照两种不同的情况讲述如何使用Kodo EJB进行开发,一种是通过命令行工具,另外一种是在Eclipse中使用Ant任务。
关于EJB3和JDO2的更多内容请大家查看文章最后参考资源中的相关内容。
为什么使用Kodo EJB
在Kodo EJB框架中,对象和关系数据库之间的映射(对象-表,对象属性-字段等)都是使用JDK5.0中的最新特性—注释(Annotation)来提供,不再需要提供额外的配置文件。
根据EJB3规范的要求,Kodo EJB除了支持在EJB容器中使用满足重量级企业应用的需求之外,也支持在普通Java应用中采用,提供轻量级的持久层框架。只不过当我们在EJB容器中使用Kodo EJB时我们需要更多的工作以便满足EJB容器的要求。
下载、安装Kodo
准备工作
由于Kodo是基于注释机制的框架,我们必须使用JDK5.0完成开发工作。所以下载、安装Kodo之前,请确保您已经下载和安装了JDK5.0.
为了演示的需要,我们选择MySQL数据库作为持久化的目标数据库,请大家自己到www.mysql.com下载最新的MySQL数据库后安装。
下载、安装Kodo
Kodo的最新版本是Kodo 4.0.0 Early Access 4,目前大家可以到http://www.solarmetric.com/去下载试用版本,下载时需要注册,你会得到30天的License.
将下载的压缩文件解压到c:/kodo4目录下(后面将使用%KODO_HOME%来引用这个目录),打开%KODO_HOME%/bin/kodocmd.cmd文件,将其中的KODODIR设置为您的Kodo的安装目录,将JDKHOME设置为Java的安装目录。
Kodo EJB实例
上面的工作完成以后,我们就可以开发Kodo EJB应用了,下面的例子将主要讲述如何开发一个轻量级的Kodo EJB例子,支持在Java应用中的调用,而不依赖于EJB容器,关于如何在EJB容器中配置使用Kodo EJB是一个大的课题,作者将另外撰文详细论述。
下面我们将用一个简单的例子来说明创建Kodo EJB应用的典型步骤,实例中我们将创建名为Book的持久化对象,该对象将被持久化到本地的MySQL数据库中。
请大家注意的是,下面的说明都基于Windows 2000操作系统,如果您使用其它操作系统,可能需要做出相应的变更。
建立工程目录在C:盘根目下创建名为KodoExamples的目录,我们所有的类文件和配置都放在这个目录下。
创建持久化类新创建持久化类Book,为了说明的简单,该类只有两个属性:id和name,其中id是书的编号(编号由MySQL数据库自动生成),name属性表示书的名称。持久化类的全部代码、注释和说明如下: ackage org.vivianj.kodo.examples.beans;import javax.persistence.Basic;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Inheritance;import javax.persistence.InheritanceType;import javax.persistence.Table;
/** * Book 用于表征系统中的书籍对象,他有两个属性id - 书籍编号,书籍编号将由MySQL数据库自动生成name - 书名*/ /* Entity注释表示该类是持久化类,的name属性是该实体在查询中对应的唯一名称,默认是类名 */(name = "Book")
/* Table注释的name属性指定该持久化类对应的数据表的名称,默认数据表名和类名保持一致,为了增强代码的可移植性,建议大家在name属性中使用大写英文字母 */(name = "BOOKS")
/* Inheritance注释的strategy确定了持久化对象和数据表之间的关系,可选择项包括SINGLE_TABLE、JOINED和TABLE_PER_CLASS,我们这里采用TABLE_PER_CLASS */(strategy = InheritanceType.TABLE_PER_CLASS)
public class Book { /* Id注释表示该字段是标识字段 */
/* GeneratedValue注释定义了该标识字段的产生方式,我们的演示系统中id由MySQL数据库字段自动生成,因此选择GenerationType.IDENTITY */(strategy = GenerationType.IDENTITY)
/* Column注释的name属性定义了该类属性对应的数据字段的名称,为了最大限度保持系统和数据库之前的独立性,建议使用大写字符 */(name = "ID")
public int id;
/* Basic注释表示该属性是基本属性 */
/* Column注释的name属性定义了该类属性对应的数据字段的名称,为了最大限度保持系统和数据库之前的独立性,建议使用大写字符 */(name = "NAME")
public String name = null;准备数据库在MySQL数据库中新建名为Kodo的数据库。
准备配置文件在C:KodoExamples新建META-INF目录,随后在该目录下新建kodo.xml和persistence.xml文件。
a)
Kodo.xml文件中提供访问数据库所需要的细节信息、使用Kodo所需要的授权(License)内容、Kodo运行时的日志管理等。
<?xml version="1.0"?> <persistence> <persistence-unit name=""> <properties> <!—— Kodo的序列号,请输入您下载或者购买Kodo时得到的License ——> <property name="kodo.LicenseKey" value="093D-BF3F-C10E-0F8F-0F00" />
<!—— 以下是访问数据库时需要提供的信息 ——> <property name="kodo.ConnectionURL" value="jdbc:mysql://localhost/kodo" /> <property name="kodo.ConnectionDriverName" value="org.gjt.mm.mysql.Driver" /> <property name="kodo.ConnectionUserName" value="root" /> <property name="kodo.ConnectionPassword" value="root" />
<!—— 设置Kodo运行过程中的日志级别 ——> <property name="kodo.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=DEBUG" /> </properties> </persistence-unit> </persistence>
b)
persistence.xml提供EJB实体管理所需要的信息,比如确定使用哪种持久化管理器和需要被管理的持久化类。
<?xml version="1.0"?> <persistence> <persistence-unit name=""> <provider>kodo.persistence.PersistenceProviderImpl</provider> <!—— 需要被Kodo EJB管理的持久化类 ——> <class>org.vivianj.kodo.examples.beans.Book</class> </persistence-unit> </persistence>编译持久化类打开一个命令行窗口,进入%KODO_HOME%/bin目录下,执行kodocmd.cmd命令,然后将MySQL的驱动文件使用set classpath=%classpath%;c:/mysql-connector-java-3.1.8-bin.jar这样的方式加入到CLASSPATH中。
执行javac C:KodoExamplesorgvivianjkodoxampleseans*.java编译持久化类。
加强(Enhancer)持久化类KODO中使用加强工具(Enhancer)对持久化类进行调整,支持性能优化、懒惰式装载等特性。
我们可以使用kodoc C:KodoExamplesorgvivianjkodoxampleseans*.java这样的命令来完成持久化类的加强工作。
生成数据库表KODO中提供了专门的MappingTool工具,能够自动的根据持久化类及他们之间的关系为我们生成创建数据库所需要的SQL语句或者直接创建数据表。
演示实例中,我们使用kodoc C:KodoExamplesorgvivianjkodoxampleseans*.java完成数据表的创建,命令执行完成后,我们访问MySQL中的kodo数据库,可以看到里面已经创建了名为books的数据表。
测试一下现在,所有的工作都已经完成,我们可以编写一段代码测试一下上面的工作是否有效,在C:kodoExamples目录下新建Test.java,然后输入如下内容:
import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;import javax.persistence.PersistenceContextType;
import org.vivianj.kodo.examples.beans.Book;
public class Test {
public static void main(String[] args) { /* 获得EJB的实体管理器 */ EntityManagerFactory emf = Persistence.createEntityManagerFactory(null);EntityManager em = emf。createEntityManager(PersistenceContextType.EXTENDED);/* 开始事务 */ em.getTransaction()。begin();
/* 创建新的持久化对象 */ Book book = new Book();/* 设置Book对象的name属性 */ book.name = "Kodo入门";/* 持久化对象 */ em.persist(book);
/* 结束事务 */ em.getTransaction()。commit();em.close();emf.close();}
}执行Test类,执行完后会发现books表中已经增加了一条新的记录。
在Eclipse中开发Kodo EJB上面的步骤都是基于命令行的,操作起来不是很方便,因此我们需要考虑将Kodo的开发过程和Eclipse开发工具进行集成。
分析上面开发Kodo EJB的整个过程,只有步骤 5)加强(Enhancer)持久化类和 6)生成数据表和字段无法在Eclipse中直接实现,查看Kodo的帮助文档,发现Kodo的发布包中已经提供了对应命令的Ant任务脚本,因此我们可以使用Ant来完成Eclipse和Kodo的集成。
我们仍然使用上面的例子来演示如何在Eclipse中开发Kodo EJB,我们打开Eclipse,新建立KodoExamples工程,将%KODO_HOME%ib下面所有jar文件加入到该工程的引用中,将您所使用的数据库的JDBC驱动jar文件也加入到该工程的引用中。然后请大家参考上面的说明完成前5步工作,下面我们主要描述如何完成Ant配置文件。
基本配置内容在工程的当前目录下,新建build.xml文件,输入如下内容:
<project name="kodo enhance" basedir="." default="enhance"> <!— 请将value的值设置为您安装kodo时选择的目录 ——> <property name="KODO_HOME" value="C:kodo4" /> <property name="src" value="." /> <property name="classes" value="." />
<!—— 准备公用的CLASSPATH路径 ?
<path id="build_classpath"> <pathelement location="" /> <!—此处请输入数据库驱动文件的路径,作者使用的MySQL数据库驱动 ?
<pathelement location="C:/ mysql-connector-java-3.1.8-bin.jar" />
<fileset dir="/lib"> <include name="**/*.jar" /> </fileset> </path> </project>编写完成步骤 6)加强(Enhance)持久化类所需要的任务和这个步骤对应的任务是kodo.ant.PCEnhancerTask类,在%KODO_HOME%srckodont下面我们可以看到PCEnhancerTask类的源代码。首先使用taskdef加入新的任务类型kodoc,然后调用该任务完成工作。
在build.xml文件中增加如下内容:
<target name="enhance"> <!—— 引入新的Ant任务 ?
<taskdef name="kodoc" classname="kodo.ant.PCEnhancerTask"> <!—— 引用上面步骤中定义的build_classpath作为CLASSPATH ?
<classpath refid="build_classpath" /> </taskdef>
<kodoc jdoEnhance="true"> <fileset dir="."> <!—— 指定需要被加强的持久化类,可以使用通配符* ?
<include name="**/Book.java" /> </fileset> <!—— 引用上面步骤中定义的build_classpath作为CLASSPATH ?
<classpath refid="build_classpath" /> </kodoc> </target>编写完成步骤 7)生成数据库表所需要的任务Kodo包中为这个任务提供的任务类kodo.jdbc.ant.MappingToolTask在Eclipse中执行时有点问题,我将它修改了一下,主要是修改执行该任务时使用的ClassLoader,现在可以满足要求了。
在当前工程中新建kodo.jdbc.ant.MappingToolTask类(目录中的类比lib文件中的类有更高的执行级别),类的代码可以在%KODO_HOME%/src/kodo/jdbc/ant目录下找到。在类源代码中找到这两行。
if (!MappingTool.run (conf, files, flags, loader))
throw new BuildException (_loc.get ("bad-conf", "MappingToolTask"));将它修改为:if (!MappingTool.run (conf, files, flags, MappingTool.class.getClassLoader ()))
throw new BuildException (_loc.get ("bad-conf", "MappingToolTask"));
现在,我们在build.xml文件中增加如下内容就可以完成项目中生成持久化类对应的数据库表了:<target name="create-schema"> <!—— 引入新的Ant任务 ?
<taskdef name="mappingtool" classname="kodo.jdbc.ant.MappingToolTask"> <!—— 引用上面步骤中定义的build_classpath作为CLASSPATH ?
<classpath refid="build_classpath" /> </taskdef>
<mappingtool action="refresh"> <!—— 引用上面步骤中定义的build_classpath作为CLASSPATH ?
<classpath refid="build_classpath" /> <fileset dir=""> <!—— 指定需要生成数据表的持久化类,可以使用通配符* ?
<include name="**/Book.java" /> </fileset> </mappingtool> </target>现在你可以将打开Eclipse中的Ant视图,然后再其中执行Enhance和create-schema任务,完成加强持久化类和创建数据库表的工作,最后,你可以使用同样的测试代码完成代码的测试工作。
总结
Kodo是BEA最新发布的持久层框架,屏蔽了开发者访问数据库的众多细节,为开发者提供了更加简单的编程模式,大大较少了开发量,目前Kodo支持EJB3和JDO2标准。BEA承诺在不久的将来将Kodo的核心代码开源。
本文中,作者通过一个简单的例子,详细地讲解了如何完成Kodo EJB的开发工作,同时讲解了如何在Eclipse中集成Kodo完成开发的步骤,并且解决了集成过程中还存在的一个小问题,希望能够帮助大家更好的学习和使用Kodo.
参考资源:
EJB3规范:http://jcp.org/aboutJava/communityprocess/pfd/jsr220/index.html
JDO2规范:http://jcp.org/aboutJava/communityprocess/pfd/jsr243/index.html
Kodo在线文档:http://solarmetric.com/kodo/Documentation/4.0.0EA4/docs/full/html/index.html
作者简介
唯J族(www.vivianj.org)创始人,BEA 杭州User Group负责人,自由撰稿人,开源项目BuildFileDesigner(buildfiledesign.sourceforge.net)和V-Security(v-security.sourceforge.net)创始人。
posted @
2006-04-13 09:03 小小程序程序员混口饭吃 阅读(250) |
评论 (0) |
编辑 收藏
牛牛的站
JDON
posted @
2006-04-13 09:02 小小程序程序员混口饭吃 阅读(183) |
评论 (0) |
编辑 收藏
JTA(Java Transaction API)是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。
JTA是Java Transaction API,是java为处理事务而引入的API,这个事务,包括分布式事务。Java内部的JDBC也提供了事务支持,但那只面向单数据源的本地事务管理。若想做分布式事务管理,必须求助于JTA。
posted @
2006-04-13 09:02 小小程序程序员混口饭吃 阅读(504) |
评论 (0) |
编辑 收藏
读了几遍struts的源代码,感觉struts还是比较容易扩充的,比如对taglib,对action servlet的扩充,对struts-config.xml的扩充都是比较容易的,但是也存在一些问题
1:struts的taglib感觉是struts里面最不灵活的(用法不灵活)
2:exception handler是个不错的设计
3:对plugin,filter的支持只是对servlet的一个扩充支持而已
4: 多种action:baseaction ,dispacth ,mappingdispach,lookup,等等提供多种支持还是不错的
5:对于doubleclick和reload的token支持感觉效果一般,并不是很灵活
6:form的使用还是有些绑定比较死
7:validator的设计也不是很灵活,感觉在action没有valitor,很不灵活,设计在Formbean中难道是为了结构?
有时间读一下webwork的源代码,看一下webwork的优缺点
posted @
2006-04-13 09:02 小小程序程序员混口饭吃 阅读(169) |
评论 (0) |
编辑 收藏
在开发ejb之前,我们先得配置好服务器,我使用的是Weblogic9.0中文版,关于Weblogic9.0配置请看我的另一片文章。
配置Weblogic9.0 首先需要配置好eclipse。我这里主要说明weblogic的配置。
注意JDK选择JDK5.0的版本。 顺便将weblogic8的配置也贴上来,供大家比较参考
注意weblogic8的JDK版本必须是JDK1.4。 接下来就开始我们的开发了。
下面就是SessionBean的代码
30
1 package com.ejb;
2
3 import java.rmi.RemoteException;
4
5 import javax.ejb.EJBException;
6 import javax.ejb.SessionBean;
7 import javax.ejb.SessionContext;
8
9 /**
10 * XDoclet-based session bean. The class must be declared
11 * public according to the EJB specification.
12 *
13 * To generate the EJB related files to this EJB:
14 * - Add Standard EJB module to XDoclet project properties
15 * - Customize XDoclet configuration for your appserver
16 * - Run XDoclet
17 *
18 * Below are the xdoclet-related tags needed for this EJB.
19 *
20 * @ejb.bean name="HelloWorld"
21 * display-name="Name for HelloWorld"
22 * description="Description for HelloWorld"
23 * jndi-name="ejb/HelloWorld"
24 * type="Stateless"
25 * view-type="remote"
26 */
27 public class HelloWorld implements SessionBean {
28
29 /** The session context */
30 private SessionContext context;
31
32 public HelloWorld() {
33 super();
34 // TODO 自动生成构造函数存根
35 }
36
37 /**
38 * Set the associated session context. The container calls this method
39 * after the instance creation.
40 *
41 * The enterprise bean instance should store the reference to the context
42 * object in an instance variable.
43 *
44 * This method is called with no transaction context.
45 *
46 * @throws EJBException Thrown if method fails due to system-level error.
47 */
48 public void setSessionContext(SessionContext newContext)
49 throws EJBException {
50 context = newContext;
51 }
52
53 public void ejbRemove() throws EJBException, RemoteException {
54 // TODO 自动生成方法存根
55
56 }
57
58 public void ejbActivate() throws EJBException, RemoteException {
59 // TODO 自动生成方法存根
60
61 }
62
63 public void ejbPassivate() throws EJBException, RemoteException {
64 // TODO 自动生成方法存根
65
66 }
67
68 /**
69 * An example business method
70 *
71 * @ejb.interface-method view-type = "remote"
72 *
73 * @throws EJBException Thrown if method fails due to system-level error.
74 */
75 public String hello() throws EJBException {
76 // rename and start putting your business logic here
77 return new String("HelloEJBWorld!");
78 }
79
80 }
81 其实就是修改了其中的一个方法:
1 /**
2 * An example business method
3 *
4 * @ejb.interface-method view-type = "remote"
5 *
6 * @throws EJBException Thrown if method fails due to system-level error.
7 */
8 public String hello() throws EJBException {
9 // rename and start putting your business logic here
10 return new String("HelloEJBWorld!");
11 }
注意:代码中的解释文字不要删除,因为XDoclet需要。配置属性
添加weblogic.jar。我的路径是:bea\weblogic90\server\lib\weblogic.jar
就下来写EJBTest类:
1 package com;
2
3 import java.rmi.RemoteException;
4 import java.util.Properties;
5
6 import javax.ejb.CreateException;
7 import javax.naming.Context;
8 import javax.naming.InitialContext;
9 import javax.naming.NamingException;
10
11 import com.interfaces.HelloWorld;
12 import com.interfaces.HelloWorldHome;
13
14 public class EJBTest {
15
16 /**
17 * @param args
18 */
19 public static void main(String[] args) {
20 // TODO 自动生成方法存根
21 Properties properties=new Properties();
22 properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
23 properties.setProperty(Context.PROVIDER_URL,"t3://localhost:7001");
24
25 Context context;
26 try {
27 context = new InitialContext(properties);
28 HelloWorldHome hwh=(HelloWorldHome)context.lookup("ejb/HelloWorld");
29 HelloWorld hw=hwh.create();
30 String s=hw.hello();
31 System.out.println(s);
32 } catch (NamingException e) {
33 // TODO 自动生成 catch 块
34 e.printStackTrace();
35 } catch (RemoteException e) {
36 // TODO 自动生成 catch 块
37 e.printStackTrace();
38 } catch (CreateException e) {
39 // TODO 自动生成 catch 块
40 e.printStackTrace();
41 }
42
43 }
44
45
46 }
47
最后就是看结果了,先启动weblogic,然后运行EJBTest程序。
作者写了一篇很好的文章:出处(
http://www.blogjava.net/rickhunter/articles/25484.html)感谢他的辛苦劳动!
posted @
2006-04-13 09:01 小小程序程序员混口饭吃 阅读(396) |
评论 (1) |
编辑 收藏
BEA WebLogic Server8.1 JMS入门
一. JMS简介
1. JMS基本概念
JMS(Java Message Service)是访问企业消息系统的标准API,它便于消息系
统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
2. JMS基本功能
JMS是用于和面向消息的中间件相互通信的应用程序接口。它既支持点对点(point-to-point)的域,又支持发布/订阅(publish/subscribe)类型的域,并且提供对下列类型的支持:经认可的消息传递,事务型消息的传递,一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与旧的后台系统相集成。
3. WebLogic JMS Server介绍
WebLogic Server8.1符合JAVA规范,并通过Sun Microsystems J2EE 1.3认
证.作为WebLogic的一部分,当然WebLogic JMS Server也完全遵从JMS规范,还支持集群,并可以应用于实际企业系统.下图是WebLogic JMS Server体系结构.图中可以看到WebLogic JMS Server主要组件有: WebLogic JMS servers(用于消息通信),Java客户端,JNDI(用于域名查找), 后备存储(用于持久消息存储,基于文件或者JDBC数据库).
二. WebLogic JMS特性
1. 消息通信模型
JMS 支持两种消息通信模型:点到点(point-to-point)(PTP)模型和发布/订阅(Pub/Sub)模型。除了下列不同之外,这两种消息通信模型非常地相似:
PTP 模型规定了一个消息只能有一个接收者;Pub/Sub 模型允许一个消息可以有多个接收者。
2. 消息组成
消息传递系统的中心就是消息。
一条 Message 分为三个组成部分:
· 头(header)是个标准字段集,客户机和供应商都用它来标识和路由消息。
· 属性(property)支持把可选头字段添加到消息。如果您的应用程序需要不使用标准头字段对消息编目和分类,您就可以添加一个属性到消息以实现这个编目和分类。提供 set<Type>Property(...) 和 get<Type>Property(...) 方法以设置和获取各种 Java 类型的属性,包括 Object。JMS 定义了一个供应商选择提供的标准属性集。
· 消息的主体(body)包含要发送给接收应用程序的内容。每个消息接口特定于它所支持的内容类型。
JMS 为不同类型的内容提供了它们各自的消息类型,但是所有消息都派生自 Message 接口。
· StreamMessage:包含 Java 基本数值流,用标准流操作来顺序的填充和读取。
· MapMessage:包含一组名/值对;名称为 string 类型,而值为 Java 的基本类型。
· TextMessage:包含一个 String。
· ObjectMessage:包含一个 Serializable Java 对象;能使用 JDK 的集合类。
· BytesMessage:包含未解释字节流: 编码主体以匹配现存的消息格式。
· XMLMessage: 包含XML内容。扩展TextMessage,XMLMessage 类型的使用,使得消息过滤非常便利。
3. 消息确认模式
非事务性会话中,应用程序创建的会话有5 种确认模式,而在事务性会话中,确认模式被忽略。
五种确认模式说明:
· AUTO_ACKNOWLEDGE:自动确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收。
· CLIENT_ACKNOWLEDGE:客户端确认模式。会话对象依赖于应用程序对被接收的消息调用一个acknowledge()方法。一旦这个方法被调用,会话会确认最后一次确认之后所有接收到的消息。这种模式允许应用程序以一个调用来接收,处理并确认一批消息。注意:在管理控制台中,如果连接工厂的Acknowledge Policy(确认方针)属性被设置为"Previous"(提前),但是你希望为一个给定的会话确认所有接收到的消息,那么就用最后一条消息来调用acknowledge()方法。
· DUPS_OK_ACKNOWLEDGE:允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。注意:如果你的应用程序无法处理重复的消息的话,你应该避免使用这种模式。如果发送消息的初始化尝试失败,那么重复的消息可以被重新发送。
· NO_ACKNOWLEDGE:不确认模式。不确认收到的消息是需要的。消息发送给一个NO_ACKNOWLEDGE 会话后,它们会被WebLogic 服务器立即删除。在这种模式下,将无法重新获得已接收的消息,而且可能导致下面的结果:1. 消息可能丢失;和(或者)另一种情况:2. 如果发送消息的初始化尝试失败,会出现重复消息被发送的情况。
· MULTICAST_NO_ACKNOWLEDGE:IP组播下的不确认模式,同样无需确认。发送给一个MULTICAST_NO_ACKNOWLEDGE会话的消息, 会共享之前所述的NO_ACKNOWLEDGE 确认模式一样的特征。这种模式支持希望通过IP 组播方式进行消息通信的应用程序,而且无需依赖会话确认提供的服务质量。注意:如果你的应用程序无法处理消息的丢失或者重复,那么你应该避免使用这种模式。如果发送消息的初始化尝试失败的话,重复的消息可能会被再次发送。
注:在上表的5 种确认模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和
CLIENT_ACKNOWLEDGE 是JMS 规范定义的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。
三. 配置JMS
1. 创建连接工厂
(1) 启动WebLogic Server8.1,登录控制台,选中JMS Connection Factories节点,点击右边的" Configure a new JMS Connection Factory...";
(2) 填写连接工厂的名称SendJMSFactory和JNDI名称SendJMSFactory,点击"Create";
(3) 勾上"myserver",将SendJMSFactory应用到myserver;
2. 定义后备存储
(1) 选中JMS Stores节点,点击右边的" Configure a new JMS Connection Factory...";
(2) 填写文件后备存储的名称SendFileStore和目录Directionary E:\BEA\user_projects\domains\mydomain\sendfilestore,点击"Create".
3. 创建JMS服务器
(1) 选中JMS Servers节点,点击右边的" Configure a new JMSServer...";
(2) 填写JMS服务器的名称SendJMSServer和Paging Store设为" SendFileStore",点击"Create";
(3) Target选中"myserver",将SendJMSServer应用到myserver.
4. 创建消息队列
(1) 展开"SendJMSServer"节点,点击" Configure a new JMS Queue...";
(2) 填写消息队列的名称SendJMSQueue和JNDI名称SendJMSQueue,点击"Create";
四. JMS应用程序
一个 JMS 应用程序由下列元素组成:
· JMS 客户机。 用 JMS API 发送和接收消息的 Java 程序。
· 非 JMS(Non-JMS)客户机。 认识到这一点很重要 - 旧的程序经常成为整个 JMS 应用程序的一部分,而且它们的包含应该在设计时预先考虑。
· 消息。 在 JMS 和非 JMS 客户机之间交换的消息的格式和内容是 JMS 应用程序设计所必须考虑的部分。
· JMS 供应商。供应商必须提供特定于其 MOM 产品的具体的实现。
· 受管对象。 消息传递系统供应商的管理员创建了一个对象,它独立于供应商专有的技术。包括连接工厂ConnectionFactory和目的Destination。
一种典型的 JMS 程序需要经过下列步骤才能开始消息产生和使用:
· 通过 JNDI 查找 ConnectionFactory。
· 通过 JNDI 查找一个或多个 Destination。
· 用 ConnectionFactory 创建一个 Connection。
· 用 Connection 创建一个或多个 Session。
· 用 Session 和 Destination 创建所需的 MessageProducer 和 MessageConsumer。
· 启动 Connection。
下面利用上面配置的JMS资源演示点对点消息发送和接收的过程。
五. 设计消息发送端
1. 使用的JMS资源
服务器URL: t3://localhost:80
连接工厂: SendJMSFactory
队列: SendJMSQueue
2. 设计步骤
· 初始化JNDI Tree
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
· lookup ConnectionFactory
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
· lookup Destination
queue = (Queue) ctx.lookup(queueName);
· 用 ConnectionFactory 创建Connection
qcon = qconFactory.createQueueConnection();
· 用 Connection 创建一个Session
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
· 用 Session 和 Destination 创建MessageProducer
qsender = qsession.createSender(queue);
· 启动 Connection。
qcon.start();
· 发送消息
msg = qsession.createTextMessage();
msg.setText(message);
qsender.send(msg);
3. 源代码
package jmssample;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/** This example shows how to establish a connection
* and send messages to the JMS queue. The classes in this
* package operate on the same JMS queue. Run the classes together to
* witness messages being sent and received, and to browse the queue
* for messages. The class is used to send messages to the queue.
*
* @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
*/
public class QueueSend
{
// Defines the JNDI context factory.
public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
// Defines the JNDI provider url.
public final static String PROVIDER_URL=" t3://localhost:80";
// Defines the JMS connection factory for the queue.
public final static String JMS_FACTORY="SendJMSFactory";
// Defines the queue.
public final static String QUEUE="SendJMSQueue";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueSender qsender;
private Queue queue;
private TextMessage msg;
/**
* Creates all the necessary objects for sending
* messages to a JMS queue.
*
* @param ctx JNDI initial context
* @param queueName name of queue
* @exception NamingException if operation cannot be performed
* @exception JMSException if JMS fails to initialize due to internal error
*/
public void init(Context ctx, String queueName)
throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qsender = qsession.createSender(queue);
msg = qsession.createTextMessage();
qcon.start();
}
/**
* Sends a message to a JMS queue.
*
* @param message message to be sent
* @exception JMSException if JMS fails to send message due to internal error
*/
public void send(String message) throws JMSException {
msg.setText(message);
qsender.send(msg);
}
/**
* Closes JMS objects.
* @exception JMSException if JMS fails to close objects due to internal error
*/
public void close() throws JMSException {
qsender.close();
qsession.close();
qcon.close();
}
/** main() method.
*
* @param args WebLogic Server URL
* @exception Exception if operation fails
*/
public static void main(String[] args) throws Exception {
InitialContext ic = getInitialContext();
QueueSend qs = new QueueSend();
qs.init(ic, QUEUE);
readAndSend(qs);
qs.close();
}
private static void readAndSend(QueueSend qs)
throws IOException, JMSException
{
BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
String line=null;
boolean quitNow = false;
do {
System.out.print("Enter message (\"quit\" to quit): ");
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
qs.send(line);
System.out.println("JMS Message Sent: "+line+"\n");
quitNow = line.equalsIgnoreCase("quit");
}
} while (! quitNow);
}
private static InitialContext getInitialContext()
throws NamingException
{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
}
}
六. 设计消息接收端
1. 使用的JMS资源
服务器URL: t3://localhost:80
连接工厂: SendJMSFactory
队列: SendJMSQueue
2. 设计步骤
· 初始化JNDI Tree
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
· lookup ConnectionFactory
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
· lookup Destination
queue = (Queue) ctx.lookup(queueName);
· 用 ConnectionFactory 创建Connection
qcon = qconFactory.createQueueConnection();
· 用 Connection 创建一个Session
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
· 用 Session 和 Destination 创建MessageConsumer
qreceiver = qsession.createReceiver(queue);
· 设置监听
qreceiver.setMessageListener(this);
· 启动 Connection
qcon.start();
3. 源代码
package jmssample;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* This example shows how to establish a connection to
* and receive messages from a JMS queue. The classes in this
* package operate on the same JMS queue. Run the classes together to
* witness messages being sent and received, and to browse the queue
* for messages. This class is used to receive and remove messages
* from the queue.
*
* @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
*/
public class QueueReceive implements MessageListener
{
// Defines the JNDI context factory.
public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
// Defines the JNDI provider url.
public final static String PROVIDER_URL=" t3://localhost:80";
// Defines the JMS connection factory for the queue.
public final static String JMS_FACTORY="SendJMSFactory";
// Defines the queue.
public final static String QUEUE="SendJMSQueue";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueReceiver qreceiver;
private Queue queue;
private boolean quit = false;
/**
* Message listener interface.
* @param msg message
*/
public void onMessage(Message msg)
{
try {
String msgText;
if (msg instanceof TextMessage) {
msgText = ((TextMessage)msg).getText();
} else {
msgText = msg.toString();
}
System.out.println("Message Received: "+ msgText );
if (msgText.equalsIgnoreCase("quit")) {
synchronized(this) {
quit = true;
this.notifyAll(); // Notify main thread to quit
}
}
} catch (JMSException jmse) {
jmse.printStackTrace();
}
}
/**
* Creates all the necessary objects for receiving
* messages from a JMS queue.
*
* @param ctx JNDI initial context
* @param queueName name of queue
* @exception NamingException if operation cannot be performed
* @exception JMSException if JMS fails to initialize due to internal error
*/
public void init(Context ctx, String queueName)
throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qreceiver = qsession.createReceiver(queue);
qreceiver.setMessageListener(this);
qcon.start();
}
/**
* Closes JMS objects.
* @exception JMSException if JMS fails to close objects due to internal error
*/
public void close()throws JMSException
{
qreceiver.close();
qsession.close();
qcon.close();
}
/**
* main() method.
*
* @param args WebLogic Server URL
* @exception Exception if execution fails
*/
public static void main(String[] args) throws Exception {
InitialContext ic = getInitialContext();
QueueReceive qr = new QueueReceive();
qr.init(ic, QUEUE);
System.out.println("JMS Ready To Receive Messages (To quit, send a \"quit\" message).");
// Wait until a "quit" message has been received.
synchronized(qr) {
while (! qr.quit) {
try {
qr.wait();
} catch (InterruptedException ie) {}
}
}
qr.close();
}
private static InitialContext getInitialContext()
throws NamingException
{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
}
}
七. 测试消息发送和接收
1. 设置WebLogic Classpath;
2. 转到发送接收程序目录编译文件;
3. 执行接受程序;
4. 打开另一窗口,执行发送程序;
5. 输入发送消息"quit",接收程序结束.
总结
本文先简要介绍了JMS的一些基本概念,继而引入了WebLogic JMS Server的体系结构和相关特性。在此基础之上,图文并茂地讲述了JMS在WebLogic Server 8.1上的配置。最后在解剖JMS应用程序框架的同时,以点对点为例演示了JMS的发送接收消息流程。
作者简介 |
| 周海根是(dev2dev论坛id:zhouhg) 长城软件系统有限公司 J2EE架构设计师, 项目经理 |
posted @
2006-04-13 09:00 小小程序程序员混口饭吃 阅读(559) |
评论 (0) |
编辑 收藏
在Struts中reset方法有什么作用(转)
大大熊 发表于 2005-6-7 17:48:00
创建人:王艺
创建时间:2003年6月15日星期日
第一步:
对象的可视范围:request、session、application、page。
Request:在一个请求周期内有效。就是从你点击页面上的一个按钮开始到服务器返回响应页面为止(包括响应页面)。
Session:在一个用户与服务器建立连接的整个过程中有效。
Application:在整个web应用程序内有效。
Page:仅在一个jsp页面内有效。
第二步:
ActionForm在你确定的有效期(可视范围)内是唯一的。
第三步:
在每次为ActionForm赋值前调用它的reset方法。作用是使ActionForm中的值恢复初始状态。在应用中我们可以通过在reset中为变量赋初值的方式,使得页面上的某个对象有显示值。
第四步:
可视范围与赋值前的初始化结合。
由于第二步所述特性,如果可视范围是request,则reset方法并不是很重要,因为你每次调用时都会产生一个新的ActionForm实例,所以你所操作的ActionForm不会与别人分享同时也就不会受别人的影响;如果可视范围是session,由于在session范围内此ActionForm是唯一的,所以你在session范围内需要用到此ActionForm的地方调用的都是同一个ActionForm,要是你没有在reset中对变量赋初值那么前一次调用ActionForm是为它赋的值将在此次调用时有效,这到也没什么。但是,如果恰巧再次调用时你仅仅需要为ActionForm中的一部分变量赋值,那么其余的变量将保持上一次得到的值,这样你就得到了一个“新旧混合体”,我想这多半不是你所期望的;如果可视范围是application,那其影响就更是不难理解了,这时不但是你自己会影响你自己,使用应用的其他用户的操作也会影响到你。
第五步:
知道了reset方法的作用和ActionForm在scope内唯一的特性后就为我们灵活处理ActionForm的行为提供了基础。比如说你现在需要跨过多个页面收集数据信息,这时你就可以把scope设置为session,并且不实现reset方法――这样在每个页面put数据时都不会将之前收集的数据清空,最后在你收集完数据后在Action中调用ActionForm中你自定义的初始化方法,如:resetField。
在具体的我也想不出了,还是要大家在应用时多多体会这些特性,这样才能把架构的威力发挥到最大。
posted @
2006-04-13 09:00 小小程序程序员混口饭吃 阅读(1747) |
评论 (0) |
编辑 收藏