import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* 本技术适用于共享数据很少更改且由多线程同时访问的场合。不过该技术仅适用于应用程序不要求使用绝对最新数据的场合。
* 最终结果是并发访问随时间变化的共享数据。在要求高并发性的环境中。该技术可以避免在应用程序内部包含不必要的排队点。
* 仅用于 Java 5.0 及更高版本。
* @author xued
*/
public class ReadNoLockingMap {
// 必须设为volatile类型以确保消费线程可以得到更新后的值
private volatile Map currentMap;
private Object lockbox;
public ReadNoLockingMap() {
this.currentMap = new HashMap();
this.lockbox = new Object();
}
public void putNewMap(Object key, Object value) {
synchronized (lockbox) {
Map newMap = new HashMap(currentMap);
newMap.put(key, value);
currentMap = newMap;
}
}
public void putNewMap(Map t) {
synchronized (lockbox) {
Map newMap = new HashMap(currentMap);
newMap.putAll (t);
currentMap = newMap;
}
}
public Object getFromCurrentMap(Object key) { // Called by consumer threads.
Map m = currentMap; // No locking around this is required.
Object result = m.get(key); // get on a HashMap is not synchronized.
return (result); // Do any additional processing needed using the result.
}
public static void main(String[] args) {
final ReadNoLockingMap nlMap = new ReadNoLockingMap();
nlMap.putNewMap("key", "Starting...");
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
int value = new Random().nextInt();
nlMap.putNewMap("key", value);
System.out.println(Thread.currentThread().getName() + ": write map " + value);
}
}).start();
}
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + ": " + nlMap.getFromCurrentMap("key"));
}
}).start();
}
}
}
2007年12月5日星期三
2007年12月3日星期一
ActiveMq+Spring2.5配置
配置Broker:
<bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean" lazy-init="false">
<property name="config" value="classpath:com/xued/techevaluate/mq/activemq.xml" />
<property name="start" value="true" />
</bean>
activemq.xml内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<broker xmlns="http://activemq.org/config/1.0" useJmx="false" persistent="false">
<transportConnectors>
<transportConnector uri="tcp://localhost:61616"/>
</transportConnectors>
</broker>
</beans>
配置ConnectionFactory:
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory ">
<property name="brokerURL" value="vm://localhost" />
<property name="copyMessageOnSend" value="false" />
<property name="objectMessageSerializationDefered" value="true" />
</bean>
<bean id="jmsTxManager" class="org.springframework.jms.connection.JmsTransactionManager" lazy-init="false">
<property name="connectionFactory" ref="jmsFactory"/>
</bean>
采用vm://localhost方式的ConnectionFactory用于同一个JVM内部的消费者
配置listener-container:
首先加入xml-schema,类似以下内容:
<beans xmlns=" http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xmlns:context="http://www.springframework.org/schema/context "
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-2.5.xsd " default-lazy-init="true">
加入listener-container内容:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor ">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
</bean>
<jms:listener-container connection-factory="jmsFactory" task-executor="taskExecutor" concurrency="10" acknowledge="transacted">
<jms:listener destination="TEST.FOO" ref="messageReceiver" method="receive" />
</jms:listener-container>
以上配置的含义是,监听名为TEST.FOO的queue,message收到后交给messageReceiver的bean处理,处理方法名为receive。
messageReceiver代码:
import java.io.Serializable;
import org.springframework.stereotype.Component;
@Component(value = "messageReceiver")
public class MessageReceiver {
public void receive(String msg) {
System.out.println(msg);
}
public void receive(Serializable msg) {
TransferedObject obj = (TransferedObject) msg;
System.out.println (obj.getMsg());
}
}
客户端配置:
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
</property>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
</bean>
使用PooledConnectionFactory的目的是避免JmsTemplate每次发送都创建一个新的连接。
客户端代码:
import com.xued.techevaluate.mq.TransferedObject;
import java.io.Serializable;
import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext ;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator ;
import org.springframework.stereotype.Component;
@Component(value = "sender")
public class jmsSender {
@Resource(name = "jmsTemplate")
private JmsTemplate jmsTemplate;
public void sendString(String queueName, final String msg) {
jmsTemplate.send(queueName, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}
});
}
public void sendObject(String queueName, final Serializable obj) {
jmsTemplate.send(queueName, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage (obj);
}
});
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("MqclientContext.xml");
jmsSender sender = (jmsSender) ctx.getBean ("sender");
for (int i = 0; i < 100; i++) {
sender.sendObject("TEST.FOO", new TransferedObject("My test queue object " + i));
}
sender.sendString("TEST.FOO", "shutdown");
}
}
<bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean" lazy-init="false">
<property name="config" value="classpath:com/xued/techevaluate/mq/activemq.xml" />
<property name="start" value="true" />
</bean>
activemq.xml内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<broker xmlns="http://activemq.org/config/1.0" useJmx="false" persistent="false">
<transportConnectors>
<transportConnector uri="tcp://localhost:61616"/>
</transportConnectors>
</broker>
</beans>
配置ConnectionFactory:
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory ">
<property name="brokerURL" value="vm://localhost" />
<property name="copyMessageOnSend" value="false" />
<property name="objectMessageSerializationDefered" value="true" />
</bean>
<bean id="jmsTxManager" class="org.springframework.jms.connection.JmsTransactionManager" lazy-init="false">
<property name="connectionFactory" ref="jmsFactory"/>
</bean>
采用vm://localhost方式的ConnectionFactory用于同一个JVM内部的消费者
配置listener-container:
首先加入xml-schema,类似以下内容:
<beans xmlns=" http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xmlns:context="http://www.springframework.org/schema/context "
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-2.5.xsd " default-lazy-init="true">
加入listener-container内容:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor ">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
</bean>
<jms:listener-container connection-factory="jmsFactory" task-executor="taskExecutor" concurrency="10" acknowledge="transacted">
<jms:listener destination="TEST.FOO" ref="messageReceiver" method="receive" />
</jms:listener-container>
以上配置的含义是,监听名为TEST.FOO的queue,message收到后交给messageReceiver的bean处理,处理方法名为receive。
messageReceiver代码:
import java.io.Serializable;
import org.springframework.stereotype.Component;
@Component(value = "messageReceiver")
public class MessageReceiver {
public void receive(String msg) {
System.out.println(msg);
}
public void receive(Serializable msg) {
TransferedObject obj = (TransferedObject) msg;
System.out.println (obj.getMsg());
}
}
客户端配置:
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
</property>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
</bean>
使用PooledConnectionFactory的目的是避免JmsTemplate每次发送都创建一个新的连接。
客户端代码:
import com.xued.techevaluate.mq.TransferedObject;
import java.io.Serializable;
import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext ;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator ;
import org.springframework.stereotype.Component;
@Component(value = "sender")
public class jmsSender {
@Resource(name = "jmsTemplate")
private JmsTemplate jmsTemplate;
public void sendString(String queueName, final String msg) {
jmsTemplate.send(queueName, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}
});
}
public void sendObject(String queueName, final Serializable obj) {
jmsTemplate.send(queueName, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage (obj);
}
});
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("MqclientContext.xml");
jmsSender sender = (jmsSender) ctx.getBean ("sender");
for (int i = 0; i < 100; i++) {
sender.sendObject("TEST.FOO", new TransferedObject("My test queue object " + i));
}
sender.sendString("TEST.FOO", "shutdown");
}
}
2007年11月21日星期三
Tomcat 6下NIO及SSL配置
Tomcat 6支持三种HTTP connector,分别是:
Java Blocking Connector Java Nio Blocking Connector APR Connector
Classname Http11Protocol Http11NioProtocol Http11AprProtocol
Tomcat Version 3.x 4.x 5.x 6.x 6.x 5.5.x 6.x
Support Polling NO YES YES
Polling Size N/A Unlimited - Restricted by mem Unlimited
Read HTTP Request Blocking Non Blocking Blocking
Read HTTP Body Blocking Blocking Blocking
Write HTTP Response Blocking Blocking Blocking
SSL Support Java SSL Java SSL Open SSL
SSL Handshake Blocking Non blocking Blocking
Max Connections maxThreads See polling size See polling size
由于Tomcat 6的缺省配置还是Blocking IO,在AprListener找到tcnative库后自动改为APR connector。因此需要修改配置使用NIO connector。修改server.xml,将原来的
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
改为
<Connector port="8080" minSpareThreads="5" maxSpareThreads="75"
maxThreads="1000" strategy="ms" maxHttpHeaderSize="8192" acceptorThreadCount="2"
emptySessionPath="true" protocol=" org.apache.coyote.http11.Http11NioProtocol"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />
以使用NIO connector,注意红色的部分。
支持SSL的步骤(转贴自:http://www.xyhot.com/article.asp?id=280):
1. 生成server key
以命令行方式切换到目录%TOMCAT_HOME%,在command命令行输入如下命令( jdk1.4以上带的工具):
keytool -genkey -alias tomcat -keyalg RSA -keypass changeit -storepass changeit -keystore server.keystore -validity 3600
用户名输入域名,如localhost(开发或测试用) 或hostname.domainname(用户拥有的域名),其它全部以 enter 跳过,最后确认,此时会在%TOMCAT_HOME%下生成server.keystore 文件。
注:参数 -validity 指证书的有效期(天),缺省有效期很短,只有90天。
2. 将证书导入的JDK的证书信任库中:
这步对于Tomcat的SSL配置不是必须。
导入过程分2步,第一步是导出证书,第二步是导入到证书信任库,命令如下:
keytool -export -trustcacerts -alias tomcat -file server.cer -keystore server.keystore -storepass changeit
keytool -import -trustcacerts -alias tomcat -file server.cer -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
如果有提示,输入Y就可以了。
其他有用keytool命令(列出信任证书库中所有已有证书,删除库中某个证书):
keytool -list -v -keystore D:/sdks/jdk1.5.0_11/jre/lib/security/cacerts
keytool -delete -trustcacerts -alias tomcat -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
3. 配置TOMCAT :
修改%TOMCAT_HOME%\conf\server.xml,以文字编辑器打开,查找这一行:
xml 代码
<!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
将之后的那段的注释去掉,并加上 keystorePass及keystoreFile属性。
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" minSpareThreads="5" maxSpareThreads="75"
enableLookups="true" disableUploadTimeout="true"
acceptCount="100" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/workbench/apache-tomcat-6.0.14/server.keystore"
keystorePass="changeit"/>
4. 访问 https://localhost:8443/ 验证配置。
- Http11Protocol 传统的Blocking IO,也是缺省配置
- Http11NioProtocol 使用JDK 1.4以后的NIO包,提供Non-Blocking的HTTP引擎
- Http11AprProtocol 使用Apache Portable Runtime库,利用本地代码提供高性能的(仍然是Blocking方式)HTTP引擎
Java Blocking Connector Java Nio Blocking Connector APR Connector
Classname Http11Protocol Http11NioProtocol Http11AprProtocol
Tomcat Version 3.x 4.x 5.x 6.x 6.x 5.5.x 6.x
Support Polling NO YES YES
Polling Size N/A Unlimited - Restricted by mem Unlimited
Read HTTP Request Blocking Non Blocking Blocking
Read HTTP Body Blocking Blocking Blocking
Write HTTP Response Blocking Blocking Blocking
SSL Support Java SSL Java SSL Open SSL
SSL Handshake Blocking Non blocking Blocking
Max Connections maxThreads See polling size See polling size
由于Tomcat 6的缺省配置还是Blocking IO,在AprListener找到tcnative库后自动改为APR connector。因此需要修改配置使用NIO connector。修改server.xml,将原来的
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
改为
<Connector port="8080" minSpareThreads="5" maxSpareThreads="75"
maxThreads="1000" strategy="ms" maxHttpHeaderSize="8192" acceptorThreadCount="2"
emptySessionPath="true" protocol=" org.apache.coyote.http11.Http11NioProtocol"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />
以使用NIO connector,注意红色的部分。
支持SSL的步骤(转贴自:http://www.xyhot.com/article.asp?id=280):
1. 生成server key
以命令行方式切换到目录%TOMCAT_HOME%,在command命令行输入如下命令( jdk1.4以上带的工具):
keytool -genkey -alias tomcat -keyalg RSA -keypass changeit -storepass changeit -keystore server.keystore -validity 3600
用户名输入域名,如localhost(开发或测试用) 或hostname.domainname(用户拥有的域名),其它全部以 enter 跳过,最后确认,此时会在%TOMCAT_HOME%下生成server.keystore 文件。
注:参数 -validity 指证书的有效期(天),缺省有效期很短,只有90天。
2. 将证书导入的JDK的证书信任库中:
这步对于Tomcat的SSL配置不是必须。
导入过程分2步,第一步是导出证书,第二步是导入到证书信任库,命令如下:
keytool -export -trustcacerts -alias tomcat -file server.cer -keystore server.keystore -storepass changeit
keytool -import -trustcacerts -alias tomcat -file server.cer -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
如果有提示,输入Y就可以了。
其他有用keytool命令(列出信任证书库中所有已有证书,删除库中某个证书):
keytool -list -v -keystore D:/sdks/jdk1.5.0_11/jre/lib/security/cacerts
keytool -delete -trustcacerts -alias tomcat -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
3. 配置TOMCAT :
修改%TOMCAT_HOME%\conf\server.xml,以文字编辑器打开,查找这一行:
xml 代码
<!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
将之后的那段的注释去掉,并加上 keystorePass及keystoreFile属性。
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" minSpareThreads="5" maxSpareThreads="75"
enableLookups="true" disableUploadTimeout="true"
acceptCount="100" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/workbench/apache-tomcat-6.0.14/server.keystore"
keystorePass="changeit"/>
4. 访问 https://localhost:8443/ 验证配置。
2007年11月16日星期五
使用JConsole远程监控Java程序
- 首先程序必须运行在Java SE 5.0以上,在启动参数中加入:-Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false 9999表示监控的端口号,要确保可以访问。上面的设置将口令验证与ssl访问关闭。
- 在客户机上启动jconsole,弹出对话框的"远程 "属性页中输入host_IP:9999, 选择"连接" ,就能查看到远程程序的运行情况了。
解释一下JConsole可以监控的常用项:
内存:
- Eden Space (heap): 内存最初从这个线程池分配给大部分对象。
- Survivor Space (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
- Tenured Generation (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。
- Permanent Generation (non-heap): 保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。
- Code Cache (non-heap): HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做"代码缓存区"(code cache)
- 名称:线程的名称
- 状态:WAITING,RUNNABLE,TIMED_WAITING,BLOCKED
- 阻塞总数:
- 等待总数:
- 堆栈
2007年11月14日星期三
使用RMI方式调用JBoss的MBean
JBoss采用JMX的MBean来进行资源管理及监控的任务,用户可以自行定义自己的MBean,在容器内执行。JBoss提供了HTTP和RMI两种接口来调用MBean,HTTP方式用于jmx-console,可以在网页上查看MBean信息并执行其中的方法。RMI接口用于远程调用,具体步骤是:
- 通过JNDI查找jmx/invoker/RMIAdaptor得到RMIAdaptor的实例
- 通过构造ObjectName对象,传入RMIAdaptor定位具体的MBean实例
- 调用RMIAdaptor的invoke()方法执行MBean的方法
import java.util.Properties;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
public class JbossJMXBrowser {
private ObjectName name;
private RMIAdaptor server;
public JbossJMXBrowser(String host, String objectName) throws Exception {
Properties props = new Properties();
props.put("java.naming.factory.initial", " org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces ");
props.put("java.naming.provider.url", host);
Context ctx = new InitialContext(props);
server = (RMIAdaptor) ctx.lookup("jmx/invoker/RMIAdaptor");
name = new ObjectName(objectName);
}
public Object invoke(String operationName, Object[] params, String[] signatures) throws Exception{
return server.invoke(name, operationName, params, signatures);
}
public MBeanInfo getMBeanInfo() throws Exception{
return server.getMBeanInfo(name);
}
private void printOperationInfo(MBeanInfo info) {
MBeanOperationInfo[] oprerations = info.getOperations ();
MBeanOperationInfo op = null;
for (MBeanOperationInfo mBeanOperationInfo : oprerations) {
op = mBeanOperationInfo;
System.out.println(op.getName() + " " + op.getReturnType());
for (MBeanParameterInfo paramInfo : op.getSignature()) {
if (paramInfo.getName().equals(paramInfo.getType())) {
System.out.println(paramInfo.getType());
} else {
System.out.println(paramInfo.getType() + " " + paramInfo.getName());
}
}
}
}
public static void main(String[] args) throws Exception {
JbossJMXBrowser browser = new JbossJMXBrowser("10.100.1.156:1099", "jboss.system:type=ServerInfo ");
MBeanInfo info = browser.getMBeanInfo();
System.out.println("MBean Name: " + info.getClassName());
browser.printOperationInfo(info);
String[] sig = {"boolean"};
Object[] opArgs = {Boolean.TRUE};
Object result = browser.invoke("listMemoryPools", opArgs, sig);
System.out.println (result);
}
}
以上代码编译运行需要jbossall-client.jar 。
2007年11月6日星期二
配置GlassFish双机集群
有两台物理计算机,主机名为host151与host164,注意GlassFish是根据主机名进行集群通信,因此在两台机各自的hosts文件中必须配置IP与主机名的映射,并保持一致。
host151作为DAS管理端,配置domain和cluster,具体步骤如下:
EJB的客户端访问代码为了支持IIOP Load-balancing,需要做一些另外的配置,示例如下:
public void sayHello() throws NamingException {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
System.setProperty("com.sun.appserv.iiop.endpoints", "10.100.1.164:33700,10.100.1.151:33700");
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ": "+getExBean(props).sayHello("Herculesx"));
}
}
private ExBeanRemote getExBean(Properties props) throws NamingException {
Context ctx = new InitialContext(props);
ExBeanRemote exBean = (ExBeanRemote) ctx.lookup("com.xued.clusteredejb.ExBeanRemote");
return exBean;
}
上面的代码有两点需要注意:
host151作为DAS管理端,配置domain和cluster,具体步骤如下:
- ant -f setup-cluster.xml 安装GlassFish为cluster模式
- asadmin start-domain domain1 启动主域
- asadmin create-cluster --host host151 --port 4848 ejb-cluster 创建一个名为ejb-cluster的集群
- asadmin create-node-agent --host host151 --port 4848 agent151 创建一个名为agent151的节点代理,靠它来管理集群里的实例,必须单独启动
- asadmin create-instance --host host151 --port 4848 --nodeagent agent151 --cluster ejb-cluster instance151 创建一个名为instance151的集群实例,由agent151管理,加入ejb-cluster集群
- asadmin start-node-agent --syncinstances=true agent151 启动agent151,syncinstances设为true是为了同时启动集群和下面的实例instance151,缺省为false
- ant -f setup-cluster.xml
- asadmin create-node-agent --host host151 --port 4848 agent164 创建名为agent164的节点代理,因为host151作为集群DAS管理端,因此本机不用启动domain
- asadmin create-instance --host host151 --port 4848 --nodeagent agent164 --cluster ejb-cluster instance164 创建一个名为instance164的集群实例,由agent164管理,加入ejb-cluster集群,注意host和port选项是指管理主机名和端口号,不是物理主机,物理主机由所属agent决定
- asadmin start-node-agent --syncinstances=true agent164 启动agent164
EJB的客户端访问代码为了支持IIOP Load-balancing,需要做一些另外的配置,示例如下:
public void sayHello() throws NamingException {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
System.setProperty("com.sun.appserv.iiop.endpoints", "10.100.1.164:33700,10.100.1.151:33700");
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ": "+getExBean(props).sayHello("Herculesx"));
}
}
private ExBeanRemote getExBean(Properties props) throws NamingException {
Context ctx = new InitialContext(props);
ExBeanRemote exBean = (ExBeanRemote) ctx.lookup("com.xued.clusteredejb.ExBeanRemote");
return exBean;
}
上面的代码有两点需要注意:
- 想要支持IIOP Load-balancing,必须配置系统环境变量com.sun.appserv.iiop.endpoints=host1:port ,host2:port...,host是物理主机IP或主机名,port是IIOP端口号,在管理端可以看到不同实例具体的值。使用org.omg.CORBA.ORBInitialHost和org.omg.CORBA.ORBInitialPort就会关闭IIOP Load-balancing。
- 客户端选择不同的主机是在lookup的时候进行的,如果想每次调用都平均分配到不同主机上,就必须每次都重新lookup,这会降低一部分性能。
2007年10月28日星期日
Spring 2.5新特性――使用注解方式的Ioc
ApplicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans"
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context "
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd" default-lazy-init="true">
<context:annotation-config />
<context:component-scan base-package="com.xued.techevaluate.ioc,com.xued.techevaluate" />
</beans>
除了要注入一些外部资源,基本上不再需要配置<bean/>项。
两个bean的代码,实现同一个接口
@Component(value="Bloomberg")
public class BloombergDataService implements MarketDataService{
@PostConstruct
private void init(){
System.out.println ("Bloomberg service initiallized");
}
public String getStockData(String stockCode) {
StringBuilder sb = new StringBuilder();
sb.append("/*************************************************************\n" +
"* Bloomberg market Data Provider *\n" +
"*************************************************************/\n");
sb.append("StockCode Company Price Amount Time ");
sb.append("\n");
sb.append(stockCode + " 长江实业 136.60 200000 "+ new Date());
return sb.toString();
}
}
@Component(value="Reuters")
public class ReutersDataService implements MarketDataService{
@PostConstruct
private void init(){
System.out.println("Reuters service initiallized");
}
public String getStockData(String stockCode) {
StringBuilder sb = new StringBuilder();
sb.append("/*************************************************************\n" +
"* Reuters market Data Provider *\n" +
"*************************************************************/\n");
sb.append("StockCode Company Price Amount Time ");
sb.append ("\n");
sb.append(stockCode + " 长江实业 136.60 200000 "+ new Date());
return sb.toString();
}
}
注入的代码:
@Component(value="dataMiner")
public class DataMiner {
@Resource(name="Reuters")
private MarketDataService dataService;
public String queryByCode(String stockCode){
return dataService.getStockData(stockCode);
}
}
采用传统方式取得bean引用:
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
instance = (DataMiner)ctx.getBean("dataMiner");
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans"
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context "
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd" default-lazy-init="true">
<context:annotation-config />
<context:component-scan base-package="com.xued.techevaluate.ioc,com.xued.techevaluate" />
</beans>
除了要注入一些外部资源,基本上不再需要配置<bean/>项。
两个bean的代码,实现同一个接口
@Component(value="Bloomberg")
public class BloombergDataService implements MarketDataService{
@PostConstruct
private void init(){
System.out.println ("Bloomberg service initiallized");
}
public String getStockData(String stockCode) {
StringBuilder sb = new StringBuilder();
sb.append("/*************************************************************\n" +
"* Bloomberg market Data Provider *\n" +
"*************************************************************/\n");
sb.append("StockCode Company Price Amount Time ");
sb.append("\n");
sb.append(stockCode + " 长江实业 136.60 200000 "+ new Date());
return sb.toString();
}
}
@Component(value="Reuters")
public class ReutersDataService implements MarketDataService{
@PostConstruct
private void init(){
System.out.println("Reuters service initiallized");
}
public String getStockData(String stockCode) {
StringBuilder sb = new StringBuilder();
sb.append("/*************************************************************\n" +
"* Reuters market Data Provider *\n" +
"*************************************************************/\n");
sb.append("StockCode Company Price Amount Time ");
sb.append ("\n");
sb.append(stockCode + " 长江实业 136.60 200000 "+ new Date());
return sb.toString();
}
}
注入的代码:
@Component(value="dataMiner")
public class DataMiner {
@Resource(name="Reuters")
private MarketDataService dataService;
public String queryByCode(String stockCode){
return dataService.getStockData(stockCode);
}
}
采用传统方式取得bean引用:
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
instance = (DataMiner)ctx.getBean("dataMiner");
Java实现UCS-2/ISO10646转UTF-8
ISO10646/UCS与UTF-8的对应关系:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-----------------------------------------------------------------------------------------------------------
| UCS-2 | UTF-8 |
|----------------------------------------------------------------------------------------------------------
| | code | 1st Byte | 2nd byte | 3rd Byte |
|--------------------------------------------------------------------------------------------------------
| 000000000aaaaaaa | 0000 - 007F | 0aaaaaaa | | |
|--------------------------------------------------------------------------------------------------------
| 00000bbbbbaaaaaa | 0080 - 07FF | 110bbbbb | 10aaaaaa | |
|--------------------------------------------------------------------------------------------------------
| ccccbbbbbbaaaaaa | 0800 - FFFF | 1110cccc | 10bbbbbb | 10aaaaaa |
|--------------------------------------------------------------------------------------------------------
public class ISO10646Decoder {
public String decode(char[] rawData) {
StringBuilder sb = new StringBuilder();
try {
for (char c : rawData) {
sb.append(new String(UCS2toUTF8Code((short) c), "UTF-8"));
}
} catch (UnsupportedEncodingException unsupportedEncodingException) {
return null; //can't reach here
}
return sb.toString();
}
public String decode(byte[] rawData) {
if(rawData.length == 0 || rawData.length % 2 != 0)
return "Not UCS-2 Code";
StringBuilder sb = new StringBuilder();
try {
for (int i = 0; i < rawData.length; i += 2) {
sb.append(new String(UCS2toUTF8Code(encodeUCS2FromByte(rawData[i], rawData[i + 1])), "UTF-8"));
}
} catch (UnsupportedEncodingException unsupportedEncodingException) {
return null; //can't reach here;
}
return sb.toString();
}
private byte[] UCS2toUTF8Code(short ucs2Code) {
byte[] utf8Code = null;
if (ucs2Code < 0 || ucs2Code > (short) 0x0800) {
utf8Code = new byte[3];
utf8Code[0] = (byte) ((convertShortToInt(ucs2Code) >>> 12) | 0xe0);
utf8Code[1] = (byte) ((convertShortToInt((short) (ucs2Code & 0x0fc0)) >>> 6) | 0x80);
utf8Code[2] = (byte) ((ucs2Code & 0x003f) | 0x80);
} else if ((short) 0x0080 > ucs2Code) {
utf8Code = new byte[1];
utf8Code[0] = (byte) ucs2Code;
} else {
utf8Code = new byte[2];
utf8Code[0] = (byte) ((ucs2Code >>> 6) | 0xc0);
utf8Code[1] = (byte) ((ucs2Code & 0x003f) | 0x80);
}
return utf8Code;
}
private short convertByteToShort(byte byteValue) {
return byteValue < 0 ? (short) (byteValue ^ 0xff00) : (short) byteValue;
}
private int convertShortToInt(short shortValue) {
return shortValue < 0 ? shortValue ^ 0xffff0000 : shortValue;
}
private short encodeUCS2FromByte(byte highByte, byte lowByte) {
return (short) ((short) (convertByteToShort(highByte) << 8) | convertByteToShort(lowByte));
}
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] tmp = {0x4f, 0x60, 0x59, 0x7d, (byte)0xff, 0x0c, 0x53, 0x17, 0x4e,
(byte)0xac, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38};
String utfStr = new ISO10646Decoder().decode(tmp);
System.out.println(utfStr);
}
}
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
-----------------------------------------------------------------------------------------------------------
| UCS-2 | UTF-8 |
|----------------------------------------------------------------------------------------------------------
| | code | 1st Byte | 2nd byte | 3rd Byte |
|--------------------------------------------------------------------------------------------------------
| 000000000aaaaaaa | 0000 - 007F | 0aaaaaaa | | |
|--------------------------------------------------------------------------------------------------------
| 00000bbbbbaaaaaa | 0080 - 07FF | 110bbbbb | 10aaaaaa | |
|--------------------------------------------------------------------------------------------------------
| ccccbbbbbbaaaaaa | 0800 - FFFF | 1110cccc | 10bbbbbb | 10aaaaaa |
|--------------------------------------------------------------------------------------------------------
public class ISO10646Decoder {
public String decode(char[] rawData) {
StringBuilder sb = new StringBuilder();
try {
for (char c : rawData) {
sb.append(new String(UCS2toUTF8Code((short) c), "UTF-8"));
}
} catch (UnsupportedEncodingException unsupportedEncodingException) {
return null; //can't reach here
}
return sb.toString();
}
public String decode(byte[] rawData) {
if(rawData.length == 0 || rawData.length % 2 != 0)
return "Not UCS-2 Code";
StringBuilder sb = new StringBuilder();
try {
for (int i = 0; i < rawData.length; i += 2) {
sb.append(new String(UCS2toUTF8Code(encodeUCS2FromByte(rawData[i], rawData[i + 1])), "UTF-8"));
}
} catch (UnsupportedEncodingException unsupportedEncodingException) {
return null; //can't reach here;
}
return sb.toString();
}
private byte[] UCS2toUTF8Code(short ucs2Code) {
byte[] utf8Code = null;
if (ucs2Code < 0 || ucs2Code > (short) 0x0800) {
utf8Code = new byte[3];
utf8Code[0] = (byte) ((convertShortToInt(ucs2Code) >>> 12) | 0xe0);
utf8Code[1] = (byte) ((convertShortToInt((short) (ucs2Code & 0x0fc0)) >>> 6) | 0x80);
utf8Code[2] = (byte) ((ucs2Code & 0x003f) | 0x80);
} else if ((short) 0x0080 > ucs2Code) {
utf8Code = new byte[1];
utf8Code[0] = (byte) ucs2Code;
} else {
utf8Code = new byte[2];
utf8Code[0] = (byte) ((ucs2Code >>> 6) | 0xc0);
utf8Code[1] = (byte) ((ucs2Code & 0x003f) | 0x80);
}
return utf8Code;
}
private short convertByteToShort(byte byteValue) {
return byteValue < 0 ? (short) (byteValue ^ 0xff00) : (short) byteValue;
}
private int convertShortToInt(short shortValue) {
return shortValue < 0 ? shortValue ^ 0xffff0000 : shortValue;
}
private short encodeUCS2FromByte(byte highByte, byte lowByte) {
return (short) ((short) (convertByteToShort(highByte) << 8) | convertByteToShort(lowByte));
}
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] tmp = {0x4f, 0x60, 0x59, 0x7d, (byte)0xff, 0x0c, 0x53, 0x17, 0x4e,
(byte)0xac, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38};
String utfStr = new ISO10646Decoder().decode(tmp);
System.out.println(utfStr);
}
}
2007年10月16日星期二
使用Ant和JUnit自动构建测试
一个简化的自动化构建过程包括:
- 从存储库取得最新的代码
- 构建代码
- 记录构建版本号
- 运行单元测试
- 上传部署版本
- 发送电子邮件
- 从存储库取得最新的代码
<cvs cvsRoot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
<cvs dest="${ws.dir}" command="update"/> <!-- 执行cvs update -->
package="moduleName"
dest="${ws.dir}"
/> <!-- 执行cvs check out--> - 构建代码
执行自定义Ant构建任务。 - 记录构建版本号
<buildnumber file="mybuild.number"/>
或<target name="baseline" description="Record build information">
<!-- The name of the file that holds the build information. If no such file exists, a new
one gets created. -->
<propertyfile file="${destination.dir}/build.info">
<!-- Initial build number is 0001. Then, any subsequent build increments
this number by one each time. -->
<entry default="0001" key="build.number" operation="+" pattern="0000" type="int" />
<!-- Records the current time to the same file. -->
<entry default="now" key="build.time" pattern=" yyyy.MM.dd-HH:mm" type="date" />
</propertyfile>
</target> - 运行单元测试
<!-- ========================================
target: auto test all test case and output report file
===================================== -->
<target name="junit and report" depends="test init, all compile">
<junit printsummary="on" fork="true" showoutput="true">
<classpath>
<fileset dir="lib" includes="**/*.jar" />
<pathelement path="${output.folder}" />
<pathelement path="${basedir}/src/resource" />
</classpath>
<formatter type="xml" />
<batchtest todir="${report.folder}">
<fileset dir="${output.folder}">
<include name="**/*Test.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.folder}">
<fileset dir="${report.folder}">
<include name="TEST-*.xml" />
</fileset>
<report format="noframes" todir="${ report.folder}" />
</junitreport>
</target> - 上传部署版本
<target name="uploadbuild" description="Upload build to an FTP server">
<!-- Upload everything under the destination.dir to the FTP server. -->
<ftp server="${ftp.hostname}" remotedir="/" userid="${ftp.username}"
password="${ ftp.userpassword}" separator="\" verbose="yes" binary="yes">
<fileset dir="${destination.dir}">
<include name="**/*.*" />
</fileset>
</ftp>
</target> - 发送电子邮件
<target name="notifyteam" description="Notify testing team of the new build">
需要javaMail.jar
<!-- Read build information from the build.info file. -->
<property file="${destination.dir}/build.info" />
<!-- Send a mail to the testing team. -->
<mail mailhost="${smtp.hostname}" mailport="${smtp.hostport}"
user="${mail.user}"
password="${mail.password}"
subject="Test build #${ build.number}"
from="${smtp.from}" tolist="${smtp.tolist}">
<message>The build #${build.number} is now available for testing.</message>
</mail>
</target>
2007年9月29日星期六
敏捷开发(一)
敏捷原则及最佳实践:
UP的四个阶段:
不符合敏捷精神的做法:
- 如果你发现自己在"迭代"项目中出现开发前确认大多数需求,编程前试图创建完整、详细的规格说明或UML模型和设计,那么说明瀑布思维已经在无情的折磨着这个项目了。无论如何声称,这都不是正常的迭代或UP项目。
- 软件通常不是可预知的或可以大规模制造的。因此,变更对于软件项目来说是永恒的,反馈和改写是成功的关键要素。
- UP提倡风险驱动(risk-driven)与客户驱动(client-driven)相结合的迭代计划。这意味着早期的迭代目标要能够识别和降低最高风险,并且能构造客户最关心的可视化特性。风险驱动迭代开发更为明确地包含了以架构为中心(architecture-centric)迭代开发的实践,意味着 早期迭代要致力于核心架构的构造、测试和稳定。因为没有稳固的架构就会带来高风险。
- 采用敏捷方法并不意味着不进行建模,这是错误的理解。
- 建模和模型的目的主要用于理解和沟通,而不是构建文档。
- 只需对设计空间中不常见、困难和棘手的一小部分问题建模和应用UML。简单的设计问题可以推延到编程阶段,在编程和测试过程中解决这些问题。
- 尽可能使用简单的工具。因为不需要耗费精力在精美的文档上。
- 建模的目的是发现、理解和共享大家的理解。因此,不要单独建模,小组成员要轮流画草图,以使每个人参与其中。
- 并行地创建模型。即同时开发类图和交互图,不断交替。
- 使用简单常用的UML元素。
- 所有模型都可能是不准确的,将其仅仅视为一种探索的过程。
- 开发者应该为 自己进行OO设计建模,而不是交给其他编程者实现。瀑布方法才会这样做。
- UP核心思想:短时间定量迭代、进化和可适应性开发。对于整个项目不应有详细的计划。应该制定估计结束日期和主要里程碑的阶段计划,但是不要对这些里程碑详细定义细粒度的步骤。只能预先对一个迭代制定更为详细的计划(迭代计划)。详细计划是由一次次迭代的调整而完成的。
- 不断地验证质量,提早、经常和实际地测试。
- 在适当的地方使用用例。
- 认真管理需求,实行变更请求和配置管理。
UP的四个阶段:
- 初始(Inception):大体的构想、业务案例、范围和模糊评估。
- 细化(Elaboration):已精化的构想、核心架构的迭代实现、高风险问题的解决、确定大多数需求和范围以及进行更为实际的评估。
- 构造(Construction):对遗留下来的风险较低和比较简单的元素进行迭代实现,准备部署。
- 移交(Transition):进行beta测试和部署。
不符合敏捷精神的做法:
- 在开始设计或实现之前试图定义大多数需求。同样,在开始实现之前试图定义大多数设计;试图在迭代编程和测试之前定义和提交完整的架构。
- 在编程之前花费数日获数周进行UML建模,或者认为在绘制UML图和进行设计时要准确完整地定义极其详细的设计和模型。并且,认为编程只是简单机械地将其转换为代码的过程。
- 认为初始阶段=需求阶段,细化阶段=设计阶段,构造阶段=实现阶段(瀑布模型的典型做法)。
- 认为细化的目的是完整仔细地定义模型,以能够在构造阶段将其转换为代码。
- 坚信合适的迭代时间长度为三个月,而不是三周。
- 认为采用UP就意味着要完成大量可能的活动和创建大量的文档,并且认为UP是需要遵循大量步骤的、正规和繁琐的过程。
- 试图对项目从开始到结束制定详细计划;试图预测所有迭代,以及每个迭代中可能发生的事情。
2007年9月24日星期一
EJB3的Timer Service
代码:
package com.xued.timer;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
@Stateless
public class TimerBean implements TimerBeanLocal {
@Resource
private TimerService ts;
public void cancelAllSchedule() {
for (Object obj : ts.getTimers()) {
Timer timer = (Timer) obj;
timer.cancel();
}
}
public void repeatSchedule() {
long start = 5000;
ts.createTimer(start, 9000, null);
System.out.println("TimerBean: Timer created for first expire after " + start + "ms");
}
@Timeout
public void handleTimeout(Timer timer) {
System.out.println("TimerBean: handle timeout occured ");
}
}
cancelAllSchedule()方法用于在设定Timer之前先取消所有关联到这个EJB的Timer,如果不这样做,则容器会将上次运行的Timer关联到这个EJB。利用这个特性,如果Timer的策略不会改变的话,则repeatSchedule()方法只需要在初次部署的时候调用一次就够了,下次重启动的时候,容器会记住你设置的策略,并自动应用到这个EJB上面。
@Timeout指定Timer到点的时候需要执行的方法。
另外,第一次部署TimerBean的时候需要调用repeatSchedule()方法来激活Timer,这就需要一个客户端,手工执行过于麻烦。在JBoss里可以写一个启动时自动执行的管理Bean,代码如下:
接口
package com.xued.timer;
import org.jboss.annotation.ejb.Management;
@Management
public interface TimerInitialService {
void create() throws Exception;
void start() throws Exception;
void stop();
void destroy();
}
实现类
package com.xued.timer;
import javax.ejb.EJB;
import org.jboss.annotation.ejb.Service;
@Service
public class TimerInitialServiceImpl implements TimerInitialService{
@EJB
TimerBeanLocal timerbean;
public void create() throws Exception {
System.out.println("TimerInitialService Started!");
timerbean.cancelAllSchedule();
timerbean.repeatSchedule();
}
public void start() throws Exception {
}
public void stop() {
}
public void destroy() {
}
}
这样启动的时候,就会自动去调用cancelAllSchedule()和repeatSchedule()两个方法,Timer就被激活了。
package com.xued.timer;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
@Stateless
public class TimerBean implements TimerBeanLocal {
@Resource
private TimerService ts;
public void cancelAllSchedule() {
for (Object obj : ts.getTimers()) {
Timer timer = (Timer) obj;
timer.cancel();
}
}
public void repeatSchedule() {
long start = 5000;
ts.createTimer(start, 9000, null);
System.out.println("TimerBean: Timer created for first expire after " + start + "ms");
}
@Timeout
public void handleTimeout(Timer timer) {
System.out.println("TimerBean: handle timeout occured ");
}
}
cancelAllSchedule()方法用于在设定Timer之前先取消所有关联到这个EJB的Timer,如果不这样做,则容器会将上次运行的Timer关联到这个EJB。利用这个特性,如果Timer的策略不会改变的话,则repeatSchedule()方法只需要在初次部署的时候调用一次就够了,下次重启动的时候,容器会记住你设置的策略,并自动应用到这个EJB上面。
@Timeout指定Timer到点的时候需要执行的方法。
另外,第一次部署TimerBean的时候需要调用repeatSchedule()方法来激活Timer,这就需要一个客户端,手工执行过于麻烦。在JBoss里可以写一个启动时自动执行的管理Bean,代码如下:
接口
package com.xued.timer;
import org.jboss.annotation.ejb.Management;
@Management
public interface TimerInitialService {
void create() throws Exception;
void start() throws Exception;
void stop();
void destroy();
}
实现类
package com.xued.timer;
import javax.ejb.EJB;
import org.jboss.annotation.ejb.Service;
@Service
public class TimerInitialServiceImpl implements TimerInitialService{
@EJB
TimerBeanLocal timerbean;
public void create() throws Exception {
System.out.println("TimerInitialService Started!");
timerbean.cancelAllSchedule();
timerbean.repeatSchedule();
}
public void start() throws Exception {
}
public void stop() {
}
public void destroy() {
}
}
这样启动的时候,就会自动去调用cancelAllSchedule()和repeatSchedule()两个方法,Timer就被激活了。
订阅:
评论 (Atom)
