求助:关于jms的持久性,和消息选择的问题

我现在正在开发一个地税的系统,有一个关于自动复业的功能,就是在纳税人报停业的时候要填一个复业的时间,然后到了那个时间后系统就能自动复业.我的考虑是写一个message bean,当用户报停业的时候发送一个消息,包含复业的日期在消息里面.然后由系统自己去比较消息,如果消息里面的时间和当前系统时间相同或小于时,就执行复业的动作.这是我整个设计的思路.发送消息,和message bean都写好后,我发现发送消息和onmessage执行消息都可以,但是一旦在onmessage中对整个topic中的消息做了日期比较后这个消息就不在了,所以在我发送消息后只能响应一次,所有的消息就全都没了,这和我的设计是完全违背的.还有当webloigc重起的时候以前的消息也都不再了,我用的是topic,因该不存在这个问题的吧?
希望有那位大哥给于解决.
所有的代码如下:

发送消息的代码如下
public void jmsTopicProductor(String strTopicInfo,String ss)
{
Context ctx = null;
TopicConnectionFactory topicFactory = null;
TopicConnection tcon = null;
TopicSession topicSession = null;
TextMessage msg = null;

try
{
ctx = new InitialContext();

topicFactory = (TopicConnectionFactory)ctx.lookup("autopracticeConnectionFactory");
tcon = topicFactory.createTopicConnection();
topicSession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);

Topic topic = (Topic)ctx.lookup("autopracticetopic");
TopicPublisher publisher= topicSession.createPublisher(topic);

msg = topicSession.createTextMessage();
tcon.start();

msg.setStringProperty("char",ss);
msg.setText(strTopicInfo);
publisher.publish(msg); //发送消息到topic
_logger.debug("the message send ok.the message value is " + strTopicInfo);
publisher.close();
topicSession.close();
tcon.close();
}
catch(NamingException e)
{
_logger.debug("the throw nameingexception.");
}
catch(JMSException e)
{
_logger.debug("the throw jmsexception.");
}

}
第一个参数是消息的内容,第二个是传入的时间转化成的字符串作为消息比较的property.消息是能发送成功的,参数传的也是正确的.

message bean 的onmessge代码如下
public void onMessage(Message incomingMsg){
TextMessage msgText = null;
StreamMessage msgObject = null;
String msgtime = null;
String nsr = null;

try {

if(incomingMsg instanceof TextMessage)
{
msgText = (TextMessage)incomingMsg;
String msgtype = msgText.getStringProperty("char");
String year = msgtype.substring(0,4);
_logger.debug("the str year is " + year);
String month = msgtype.substring(4,6);
_logger.debug("the str month is" + month);
String day = msgtype.substring(6,8);
_logger.debug("the str day is " + day);


GregorianCalendar curCalendar = new GregorianCalendar();
Date curDate = (Date) curCalendar.getTime();
DateConvert dateConvert = new DateConvert(curDate);
String nyear = dateConvert.getYear();
String nmonth = dateConvert.getMonth();
if(nmonth.length()==1)nmonth="0"+nmonth;
String nday = dateConvert.getDay();
if(nday.length()==1)nday="0"+nday;
_logger.debug("the year is " + nyear);
_logger.debug("the month is " + nmonth);
_logger.debug("the day is "+ nday);

if(day.equals(nday))
{
msgtime = msgText.getText();
_logger.debug("MMMMMMMMMMM MMMMMMMMMM get the message ok value is "+msgtime);
System.out.println("get the message ok value is"+msgtime);
}
else
{
_logger.debug("it is not.");
}
}


} catch (JMSException e) {
e.printStackTrace(); //To change body of catch statement use Options | File Templates.
} catch (Throwable te){
te.printStackTrace();
}

}

简单的做了一下比较,就是当传入的时间字符串和系统时间字符串相同的时候,就会执行,这也是可以的但是每次都会把topic中的所有消息都执行了
ejb-jar 的代码如下:
<?xml version="1.0"?>

<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>AutoPracticeMSB</ejb-name>
<ejb-class>com.jt.msg.autopractice.AutoPracticeBean</ejb-class>
<transaction-type>Container</transaction-type>
<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
</message-driven-destination>

</message-driven>
</enterprise-beans>

<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>AutoPracticeMSB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

weblogic-ejb-jar 代码如下:
<?xml version="1.0"?>

<!DOCTYPE weblogic-ejb-jar PUBLIC
'-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN'
'http://www.bea.com/servers/wls600/dtd/weblogic-ejb-jar.dtd'>

<weblogic-ejb-jar>

<weblogic-enterprise-bean>
<ejb-name>AutoPracticeMSB</ejb-name>
<message-driven-descriptor>
<destination-jndi-name>autopracticetopic</destination-jndi-name>
<!--
<connection-factory-jndi-name>autopracticeConnectionFactory</connection-factory-jndi-name>
-->
</message-driven-descriptor>

</weblogic-enterprise-bean>

</weblogic-ejb-jar>

webloigc的config.xml的关于jms的配置如下:
<JMSConnectionFactory Name="autopracticeConnectionFactory"
MessagesMaximum="-1" JNDIName="autopracticeConnectionFactory" Targets="myserver"/>
<JMSFileStore Name="MyJMSFileStore" Directory="C:\bea702\jmsStore" SynchronousWritePolicy="Direct-Write"/>
<JMSServer Name="MyJMSServer" Store="MyJMSFileStore" Targets="myserver">
<JMSTopic Name="autopracticetopic" JNDIName="autopracticetopic" StoreEnabled="true"/>
</JMSServer>

这是整个bean的代码,我想解决的问题就是
1:发送到topic中的消息,如果我不手动删除是不会删除的,也就是说系统不会自动的把我发送的消息给over掉,或者说我用过的消息删除,还没有处理的消息留在topic中
2:我怎样可以用消息选择机制,自动过虑我的消息让到时间的才去执行
3:即时我的webloigc 死掉了,没有执行的消息在webloigc启动后仍然能够继续执行
希望各位前辈帮帮小弟,尽快解决一下问题
小弟在此先谢过.

这本来就是消息处理的机制,消息一旦被就不能找回来,也不应该找回来,可以把消息处理后存储入数据库,通过数据库触发器来产生复业消息。
仅仅依赖JMS处理你的问题很不合理

那你的意思是只要消息onmessage过这后,message就over了是么?
那消息过虑不是可以在message server 中过虑如果合适的才发给message bean的么?,如果我这样过虑的话,是不是就可以实现我的想法了那?

right, you can use message-selector. But still, once message is consumed, it is deleted

Hehe, your design's problem. You cannot always compare date to determine if the account should be re-activated.

If you want to use MDB, one solution is to use the Time to Deliver feature in weblogic MDB to schedule a message that will be consumed exactly after the re-activate date is passed.

But the simple soultion would be change your database and add the life cycle fields for your records.

复业不会精确到小时吧?可以放在每日批处理中处理,也不一定要用java 来做。
对你的需求不是很明确。

我觉得需要使用最新J2EE 1.4的计时功能,或者你在web容器启动定时功能,定期读取一下数据库中所有停业的记录,发送到MDB去检查核对。

先谢谢前面几位大哥给的建议。对于xok的建议我还有一些不太明白的地方,希望能很在进行更详细的一些讨论。
看了你的建议后我自己的理解是这样的:
1 用message-selector 是完全可以的,如果条件合理的话,发送的消息全部都在message server中保存,而不去让message bean处理,当时机成熟后message bean才会去响应。如果是这样的话,那么我设想的就完全可以实现。呵呵,不过小弟对message-selector的用法有太多的不明白,希望能得到关于message-selector更详细的解释,或给个例子看看:)。
2 所说的Time to Deliver 我就更不太明白了,是weblgoic具有这中功能,还是要用到timer类来做这个实现。如果要用到timer类,在我原有的系统里面我是不是还要在单独开一个线程专门做timer的处理?把所有要处理的数据在timer的timertask对象中去执行?
3 在db中的life cycle fields 到底是个什么东东?
希望xok能在帮小兄弟一把,也希望更多的朋友来讨论这个问题

banq大哥说的web的定时功能具体指的是什么?
是不是就是timer类?每一段时间定时触发,在发消息到mdb去处理?
这样如果可以的话,我就可以不用mdb了,直接触发后就可以把需要复业的记录处理掉就ok了,这样做的话是不是比mdb的效率还要高呢?

使用MDB效率要高,在web的定时只是一个线程,如果在这个线程做太多事情不好,这个线程只起激活触发作用,前面我考虑有误,查询数据库等其它操作动作都交给MDB做。

J2EE 1.4 Timer可以有定时功能,查询一下可以知道,但是容器不一定支持1.4标准。

If you are using weblogic, please refer to:
http://e-docs.bea.com/wls/docs81/jms/implement.html#1235262
and http://edocs.bea.com/wls/docs81/jms/implement.html#1186875

To use message selector, please be carefule about the seetings on message redelivery, since you might want the producer to re-send message every day so that the consumer can do the selection every day. Another thing is message selectin criteria should only exists in the message header.

DB soultion is more or less the most common one to use. In your table, for example COMPANY_TAXABLE, add a date field such as EFFECTIVE_FROM. Append this criteria in your SQL to filter out non effective companies.

Are you from 郑州? Me too... Nice to meet you here.

EJB2.1 规范中提出了 TIMER SERVICE ,但目前的应用服务器中还没支持,
用xok提出的是方案可以解决你的问题

如果你对classloader不是很熟,建议不要在weblogic的web container里
用timer,weblogic里对于用户自己handler的Thread采用新的classloader
这个classloader无法进行正确的InitialContext,如果一定要采用,需要将Thread的classloader switch to web server 的classloader。

剩下两种方式都不错,用message-selector配置上要注意,redelivery的时间间隔和重发次数

to xok:
acknowledge mode 不需要特别设置吗,这个我不太确定了