|
2006年8月25日
当前,CORBA、DCOM、RMI等RPC中间件技术已广泛应用于各个领域。但是面对规模和复杂度都越来越高的分布式系统,这些技术也显示出其局限性:(1)同步通信:客户发出调用后,必须等待服务对象完成处理并返回结果后才能继续执行;(2)客户和服务对象的生命周期紧密耦合:客户进程和服务对象进程都必须正常运行;如果由于服务对象崩溃或者网络故障导致客户的请求不可达,客户会接收到异常;(3)点对点通信:客户的一次调用只发送给某个单独的目标对象。 面向消息的中间件(Message Oriented Middleware,MOM)较好的解决了以上问题。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接收者。这种模式下,发送和接收是异步的,发送者无需等待;二者的生命周期未必相同:发送消息的时候接收者不一定运行,接收消息的时候发送者也不一定运行;一对多通信:对于一个消息可以有多个接收者。 已有的MOM系统包括IBM的MQSeries、Microsoft的MSMQ和BEA的MessageQ等。由于没有一个通用的标准,这些系统很难实现互操作和无缝连接。Java Message Service(JMS)是SUN提出的旨在统一各种MOM系统接口的规范,它包含点对点(Point to Point,PTP)和发布/订阅(Publish/Subscribe,pub/sub)两种消息模型,提供可靠消息传输、事务和消息过滤等机制。
1.JMS JAVA 消息服务(JMS)定义了Java 中访问消息中间件的接口。JMS 只是接口,并没有给予实现,实现JMS 接口的消息中间件称为JMS Provider,iLink实现了JMS接口,用户可以通过使用JMS接口,在iLink中进行JMS编程。 iLink支持JMS1.0.2版本。
2.JMS接口描述 JMS 支持两种消息类型PTP 和Pub/Sub,分别称作:PTP Domain 和Pub/Sub Domain,这两种接口都继承统一的JMS父接口,JMS 主要接口如下所示:
MS父接口
|
PTP
|
Pub/Sub
|
ConnectionFactory
|
QueueConnectionFactory
|
TopicConnectionFactory
|
Connection
|
QueueConnection
|
TopicConnection
|
Destination
|
Queue
|
Topic
|
Session
|
QueueSession
|
TopicSession
|
MessageProducer
|
QueueSender
|
TopicPublisher
|
MessageConsumer
|
QueueReceiver,QueueBrowse r
|
TopicSubscriber
|
|
ConnectionFactory :连接工厂,JMS 用它创建连接 Connection :JMS 客户端到JMS Provider 的连接 Destination :消息的目的地 Session: 一个发送或接收消息的线程 MessageProducer: 由Session 对象创建的用来发送消息的对象 MessageConsumer: 由Session 对象创建的用来接收消息的对象
3.JMS消息模型 JMS 消息由以下几部分组成:消息头,属性,消息体。 3.1 消息头(Header) - 消息头包含消息的识别信息和路由信息,消息头包含一些标准的属性如:JMSDestination,JMSMessageID 等。
消息头
|
由谁设置
|
JMSDestination
|
send 或 publish 方法
|
JMSDeliveryMode
|
send 或 publish 方法
|
JMSExpiration
|
send 或 publish 方法
|
JMSPriority
|
send 或 publish 方法
|
JMSMessageID
|
send 或 publish 方法
|
JMSTimestamp
|
send 或 publish 方法
|
JMSCorrelationID
|
客户
|
JMSReplyTo
|
客户
|
JMSType
|
客户
|
JMSRedelivered
|
JMS Provider
|
|
3.2 属性(Properties) - 除了消息头中定义好的标准属性外,JMS 提供一种机制增加新属性到消息头 中,这种新属性包含以下几种: 1. 应用需要用到的属性; 2. 消息头中原有的一些可选属性; 3. JMS Provider 需要用到的属性。 标准的JMS 消息头包含以下属性:
JMSDestination
|
消息发送的目的地
|
JMSDeliveryMode
|
传递模式, 有两种模式: PERSISTENT 和NON_PERSISTENT,PERSISTENT 表示该消息一定要被送到目的地,否则会导致应用错误。NON_PERSISTENT 表示偶然丢失该消息是被允许的,这两种模式使开发者可以在消息传递的可靠性和吞吐量之间找到平衡点。
|
JMSMessageID
|
唯一识别每个消息的标识,由JMS Provider 产生。
|
JMSTimestamp
|
一个消息被提交给JMS Provider 到消息被发出的时间。
|
JMSCorrelationID
|
用来连接到另外一个消息,典型的应用是在回复消息中连接到原消息。
|
JMSReplyTo
|
提供本消息回复消息的目的地址
|
JMSRedelivered
|
如果一个客户端收到一个设置了JMSRedelivered 属性的消息,则表示可能该客户端曾经在早些时候收到过该消息,但并没有签收(acknowledged)。
|
JMSType
|
消息类型的识别符。
|
JMSExpiration
|
消息过期时间,等于QueueSender 的send 方法中的timeToLive 值或TopicPublisher 的publish 方法中的timeToLive 值加上发送时刻的GMT 时间值。如果timeToLive值等于零,则JMSExpiration 被设为零,表示该消息永不过期。如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被清除。
|
JMSPriority
|
消息优先级,从0-9 十个级别,0-4 是普通消息,5-9 是加急消息。JMS 不要求JMS Provider 严格按照这十个优先级发送消息,但必须保证加急消息要先于普通消息到达。
|
|
3.3 消息体(Body) - JMS API 定义了5种消息体格式,也叫消息类型,你可以使用不同形式发送接收 数据并可以兼容现有的消息格式,下面描述这5种类型:
消息类型
|
消息体
|
TextMessage
|
java.lang.String对象,如xml文件内容
|
MapMessage
|
名/值对的集合,名是String对象,值类型可以是Java任何基本类型
|
BytesMessage
|
字节流
|
StreamMessage
|
Java中的输入输出流
|
ObjectMessage
|
Java中的可序列化对象
|
Message
|
没有消息体,只有消息头和属性
|
|
下例演示创建并发送一个TextMessage到一个队列: TextMessage message = queueSession.createTextMessage(); message.setText(msg_text); // msg_text is a String queueSender.send(message);
下例演示接收消息并转换为合适的消息类型: Message m = queueReceiver.receive(); if (m instanceof TextMessage) { TextMessage message = (TextMessage) m; System.out.println("Reading message: " + message.getText()); } else { // Handle error }
4. 消息的同步异步接收 消息的同步接收是指客户端主动去接收消息,JMS 客户端可以采用MessageConsumer 的receive方法去接收下一个消息。 消息的异步接收是指当消息到达时,主动通知客户端。JMS 客户端可以通过注册一个实 现MessageListener 接口的对象到MessageConsumer,这样,每当消息到达时,JMS Provider 会调用MessageListener中的onMessage 方法。
5. PTP模型 PTP(Point-to-Point)模型是基于队列的,发送方发消息到队列,接收方从队列接收消息,队列的存在使得消息的异步传输成为可能。和邮件系统中的邮箱一样,队列可以包含各种消息,JMS Provider 提 供工具管理队列的创建、删除。JMS PTP 模型定义了客户端如何向队列发送消息,从队列接收消息,浏览队列中的消息。 下面描述JMS PTP 模型中的主要概念和对象:
名称
|
描述
|
Queue
|
由JMS Provider 管理,队列由队列名识别,客户端可以通过JNDI 接口用队列名得到一个队列对象。
|
TemporaryQueue
|
由QueueConnection 创建,而且只能由创建它的QueueConnection 使用。
|
QueueConnectionFactory
|
客户端用QueueConnectionFactory 创建QueueConnection 对象。
|
QueueConnection
|
一个到JMS PTP provider 的连接,客户端可以用QueueConnection 创建QueueSession 来发送和接收消息。
|
QueueSession
|
提供一些方法创建QueueReceiver 、QueueSender、QueueBrowser 和TemporaryQueue。如果在QueueSession 关闭时,有一些消息已经被收到,但还没有被签收(acknowledged),那么,当接收者下次连接到相同的队列时,这些消息还会被再次接收。
|
QueueReceiver
|
客户端用QueueReceiver 接收队列中的消息,如果用户在QueueReceiver 中设定了消息选择条件,那么不符合条件的消息会留在队列中,不会被接收到。
|
QueueSender
|
客户端用QueueSender 发送消息到队列。
|
QueueBrowser
|
客户端可以QueueBrowser 浏览队列中的消息,但不会收走消息。
|
QueueRequestor
|
JMS 提供QueueRequestor 类简化消息的收发过程。QueueRequestor 的构造函数有两个参数:QueueSession 和queue,QueueRequestor 通过创建一个临时队列来完成最终的收发消息请求。
|
可靠性(Reliability)
|
队列可以长久地保存消息直到接收者收到消息。接收者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势。
|
|
6. PUB/SUB模型 JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息,这些节点被称作主题(topic)。 主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息。主题使得消息订阅者和消息发布者保持互相独立,不需要接触即可保证消息的传送。 下面描述JMS Pub/Sub 模型中的主要概念和对象:
名称
|
描述
|
订阅(subscription)
|
消息订阅分为非持久订阅(non-durable subscription)和持久订阅(durable subscrip-tion),非持久订阅只有当客户端处于激活状态,也就是和JMS Provider 保持连接状态才能收到发送到某个主题的消息,而当客户端处于离线状态,这个时间段发到主题的消息将会丢失,永远不会收到。持久订阅时,客户端向JMS 注册一个识别自己身份的ID,当这个客户端处于离线时,JMS Provider 会为这个ID 保存所有发送到主题的消息,当客户再次连接到JMS Provider时,会根据自己的ID 得到所有当自己处于离线时发送到主题的消息。
|
Topic
|
主题由JMS Provider 管理,主题由主题名识别,客户端可以通过JNDI 接口用主题名得到一个主题对象。JMS 没有给出主题的组织和层次结构的定义,由JMS Provider 自己定义。
|
TemporaryTopic
|
临时主题由TopicConnection 创建,而且只能由创建它的TopicConnection 使用。临时主题不能提供持久订阅功能。
|
TopicConnectionFactory
|
客户端用TopicConnectionFactory 创建TopicConnection 对象。
|
TopicConnection
|
TopicConnection 是一个到JMS Pub/Sub provider 的连接,客户端可以用TopicConnection创建TopicSession 来发布和订阅消息。
|
TopicSession
|
TopicSession 提供一些方法创建TopicPublisher、TopicSubscriber、TemporaryTopic 。它还提供unsubscribe 方法取消消息的持久订阅。
|
TopicPublisher
|
客户端用TopicPublisher 发布消息到主题。
|
TopicSubscriber
|
客户端用TopicSubscriber 接收发布到主题上的消息。可以在TopicSubscriber 中设置消息过滤功能,这样,不符合要求的消息不会被接收。
|
Durable TopicSubscriber
|
如果一个客户端需要持久订阅消息,可以使用Durable TopicSubscriber,TopSession 提供一个方法createDurableSubscriber创建Durable TopicSubscriber 对象。
|
恢复和重新派送(Recovery and Redelivery)
|
非持久订阅状态下,不能恢复或重新派送一个未签收的消息。只有持久订阅才能恢复或重新派送一个未签收的消息。
|
TopicRequestor
|
JMS 提供TopicRequestor 类简化消息的收发过程。TopicRequestor 的构造函数有两个参数:TopicSession 和topic。TopicRequestor 通过创建一个临时主题来完成最终的发布和接收消息请求。
|
可靠性(Reliability)
|
当所有的消息必须被接收,则用持久订阅模式。当丢失消息能够被容忍,则用非持久订阅模式。
|
|
7. 开发JMS的步骤 广义上说,一个JMS 应用是几个JMS 客户端交换消息,开发JMS 客户端应用由以下几步构成: 用JNDI 得到ConnectionFactory 对象; 用JNDI 得到目标队列或主题对象,即Destination 对象; 用ConnectionFactory 创建Connection 对象; 用Connection 对象创建一个或多个JMS Session; 用Session 和Destination 创建MessageProducer 和MessageConsumer; 通知Connection 开始传递消息。
Some XA JDBC drivers do not support local transaction operations, which can cause an error similar to the following when optimistic concurrency is used with such a driver:
SQL operations are not allowed with no global transaction by default for XA drivers.
In other words, the error will occur when
SupportsLocalTransactions="true"
is specified for the JDBCConnectionPool.
This problem occurs because optimistic concurrency suspends a global transaction and does reads in a local transaction when the database is not Oracle. (When using Oracle, you can avoid this problem by explicitly setting
<database-type>Oracle</database-type>
in your CMP deployment descriptor.)
Workaround
: Use the "RollbackLocalTxUponConnClose" on the JDBCConnection.
在谈到
XA
规范之前,必须首先了解分布式事务处理(
Distributed Transaction Processing
,
DTP
)的概念。
Transaction
,即事务,又称之为交易,指一个程序或程序段,在一个或多个资源如
数据库
或文件上为完成某些功能的执行过程的集合。
LU
人的博客
)vqfhOBA
分布式事务处理是指一个事务可能涉及多个数据库操作,分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)。
X/Open
组织(即现在的
Open Group
)定义了分布式事务处理模型。
X/Open DTP
模型(
1994
)包括应用程序(
AP
)、事务管理器(
TM
)、资源管理器(
RM
)、通信资源管理器(
CRM
)四部分。一般,常见的事务管理器(
TM
)是交易中间件,常见的资源管理器(
RM
)是数据库,常见的通信资源管理器(
CRM
)是消息中间件。
通常把一个数据库内部的事务处理,如对多个表的操作,作为本地事务看待。数据库的事务处理对象是本地事务,而分布式事务处理的对象是全局事务。
所谓全局事务,是指分布式事务处理环境中,多个数据库可能需要共同完成一个工作,这个工作即是一个全局事务,例如,一个事务中可能更新几个不同的数据库。对数据库的操作发生在系统的各处但必须全部被提交或回滚。此时一个数据库对自己内部所做操作的提交不仅依赖本身操作是否成功,还要依赖与全局事务相关的其它数据库的操作是否成功,如果任一数据库的任一操作失败,则参与此事务的所有数据库所做的所有操作都必须回滚。
LU
人的博客
8v:d?(~;~$A-Q9V D
一般情况下,某一数据库无法知道其它数据库在做什么,因此,在一个
DTP
环境中,交易中间件是必需的,由它通知和协调相关数据库的提交或回滚。而一个数据库只将其自己所做的操作(可恢复)影射到全局事务中。
XA
就是
X/Open DTP
定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。
XA
接口函数由数据库厂商提供。
XA
与两阶段提交协议
通常情况下,交易中间件与数据库通过
XA
接口规范,使用两阶段提交来完成一个全局事务,
XA
规范的基础是两阶段提交协议。
在第一阶段,交易中间件请求所有相关数据库准备提交(预提交)各自的事务分支,以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预提交后,如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来,并给交易中间件一个同意提交的应答,此时数据库将不能再在该事务分支中加入任何操作,但此时数据库并没有真正提交该事务,数据库对共享资源的操作还未释放(处于上锁状态)。如果由于某种原因数据库无法提交属于自己的事务分支,它将回滚自己的所有操作,释放对共享资源上的锁,并返回给交易中间件失败应答。
在第二阶段,交易中间件审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。
以一个全局事务为例,
AP
首先通知交易中间件开始一个全局事务,交易中间件通过
XA
接口函数通知数据库开始事务,然后
AP
可以对数据库管理的资源进行操作,数据库系统记录事务对本地资源的所有操作。操作完成后交易中间件通过
XA
接口函数通知数据库操作完成。交易中间件负责记录
AP
操作过哪些数据库(事务分支)。
AP
根据情况通知交易中间件提交该全局事务,交易中间件会通过
XA
接口函数要求各个数据库做预提交,所有数据库返回成功后要求各个数据库做正式提交,此时一笔全局事务结束。
XA
规范对应用来说,最大好处在于事务的完整性由交易中间件和数据库通过
XA
接口控制,
AP
只需要关注与数据库的应用逻辑的处理,而无需过多关心事务的完整性,应用设计开发会简化很多。
具体来说,如果没有交易中间件,应用系统需要在程序内部直接通知数据库开始、结束和提交事务,当出现异常情况时必须由专门的程序对数据库进行反向操作才能完成回滚。如果是有很多事务分支的全局事务,回滚时情况将变得异常复杂。而使用
XA
接口,则全局事务的提交是由交易中间件控制,应用程序只需通知交易中间件提交或回滚事务,就可以控制整个事务(可能涉及多个异地的数据库)的全部提交或回滚,应用程序完全不用考虑冲正逻辑。
在一个涉及多个数据库的全局事务中,为保证全局事务的完整性,由交易中间件控制数据库做两阶段提交是必要的。但典型的两阶段提交,对数据库来说事务从开始到结束(提交或回滚)时间相对较长,在事务处理期间数据库使用的资源(如逻辑日志、各种锁),直到事务结束时才会释放。因此,使用典型的两阶段提交相对来说会占用更多的资源,在网络条件不是很好,如低速网、网络颠簸频繁,情况会更为严重。
当一个全局事务只涉及一个数据库时,有一种优化方式,即一阶段提交。当
AP
通知交易中间件提交事务时,交易中间件直接要求数据库提交事务,省去两阶段提交中的第一阶段,可以缩短处理一个事务的时间,以提高事务处理的效率。作为两阶段提交的一种特例,与两阶段一样,一阶段提交也是标准的。
If you encountered “
Cannot call Connection.commit in distributed transaction
” error, should check the weblogic connection pool and the datasource
If the connection pool is XA,
you should make sure that when you create the datasource to make it XA by select the check from "Honor Global Transaction" from the weblogic console.(default is selected)
If the connection pool is non-XA
you should make sure that when you create the datasource to make it non-XA by remove the check from "Honor Global Transaction" from the weblogic console.
问题描述:
<[ServletContext(id=18489944,name=EBLGWeb,context-path=/)]: Deployment descriptor "/WEB-INF/bhr-tags-pagination.tld" is malformed. Check against the DTD: Content is not allowed in prolog. (line 1, column 1).>
<[ServletContext(id=18489944,name=EBLGWeb,context-path=/)]: Error while parsing the Tag Library Descriptor at "/WEB-INF/bhr-tags-pagination.tld".
org.xml.sax.SAXException: [HTTP:101248][ServletContext(id=18489944,name=EBLGWeb,context-path=/)]: Deployment descriptor "/WEB-INF/bhr-tags-pagination.tld" is malformed. Check against the DTD: Content is not allowed in prolog. (line 1, column 1).
at weblogic.apache.xerces.parsers.DOMParser.parse(DOMParser.java:285)
at weblogic.apache.xerces.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:201)
at weblogic.servlet.jsp.dd.JSPEntityResolver.load(JSPEntityResolver.java:81)
at weblogic.servlet.jsp.dd.JSPEntityResolver.load(JSPEntityResolver.java:96)
at weblogic.servlet.internal.WebAppHelper.registerTagLibListeners(WebAppHelper.java:293)
at weblogic.servlet.internal.WebAppServletContext.activateFromDescriptors(WebAppServletContext.java:2530)
at weblogic.servlet.internal.WebAppServletContext.activate(WebAppServletContext.java:6163)
at weblogic.servlet.internal.WebAppServletContext.setActive(WebAppServletContext.java:6141)
at weblogic.servlet.internal.WebAppModule.activate(WebAppModule.java:836)
at weblogic.j2ee.J2EEApplicationContainer.activateModule(J2EEApplicationContainer.java:3322)
at weblogic.j2ee.J2EEApplicationContainer.activate(J2EEApplicationContainer.java:2201)
at weblogic.j2ee.J2EEApplicationContainer.activate(J2EEApplicationContainer.java:2174)
at weblogic.j2ee.J2EEApplicationContainer.activate(J2EEApplicationContainer.java:2122)
at weblogic.management.deploy.slave.SlaveDeployer$Application.setActivation(SlaveDeployer.java:3099)
at weblogic.management.deploy.slave.SlaveDeployer.setActivationStateForAllApplications(SlaveDeployer.java:1768)
at weblogic.management.deploy.slave.SlaveDeployer.resume(SlaveDeployer.java:351)
at weblogic.management.deploy.DeploymentManagerServerLifeCycleImpl.resume(DeploymentManagerServerLifeCycleImpl.java:229)
at weblogic.t3.srvr.SubsystemManager.resume(SubsystemManager.java:136)
at weblogic.t3.srvr.T3Srvr.resume(T3Srvr.java:965)
at weblogic.t3.srvr.T3Srvr.run(T3Srvr.java:360)
at weblogic.Server.main(Server.java:32)
>
原因分析和解决方法:
是由于某些文本编辑器(UltraEdit)存文本的时候产生的问题。可以使用text pad, word pad编辑XML格式的文件。
问题描述:
通过HTTP提交EDI请求,当EDI处理时间超过了20分钟后,就会看到在另一个weblogic server也接收到了这个EDI请求,重新处理这个报文.
如果报文处理时间更长,比如超过了50分钟,就会发现这个报文被处理了3遍或5遍.
原因分析和解决方法:
When the Apache HTTP Server Plug-In attempts to connect to WebLogic Server, the plug-in uses several configuration
parameters to determine how long to wait for connections to the WebLogic Server host and, after a connection is
established, how long the plug-in waits for a response. If the plug-in cannot connect or does not receive a response,
the plug-in attempts to connect and send the request to other WebLogic Server instances in the cluster. If the
connection fails or there is no response from any WebLogic Server in the cluster, an error message is sent.
响应等待时间参数WLIOTimeoutSecs (http://e-docs.bea.com/wls/docs81/plugins/plugin_params.html#1149781.
按照BEA 推荐的,这个值应该设得大一些, 这个参数的缺省值为300s. 见下面:
<Location /EDIHandler>
SetHandler weblogic-handler
WebLogicCluster apc_app1wls1:7011,apc_app1wls2:7011
KeepAliveEnabled ON
WLIOTimeoutSecs 7200
</Location>
有时候使用的mq jar文件的版本不对给出现一些末名奇妙的问题,本文则是能接收消息但不能发送消息的一个问题.碰到这样的问题一定要记得double-check你使用的mq jar文件的版本
问题描述:
MQJMS200713 Oct 2006 16:47:15,484 ERROR MessagingUtilServlet [ExecuteThread: '14' for queue: 'weblogic.kernel.Default'][]: **********EXCEPTION TRACE START*************
13 Oct 2006 16:47:15,484 ERROR MessagingUtilServlet [ExecuteThread: '14' for queue: 'weblogic.kernel.Default'][]: produce error
com.bhr.infra.messaging.exception.PMException: JMSException
at com.bhr.infra.messaging.PMHandler.send(PMHandler.java:419)
at com.bhr.epc.infra.messaging.util.MessagingUtilServlet.produce(MessagingUtilServlet.java:312)
at com.bhr.epc.infra.messaging.util.MessagingUtilServlet.service(MessagingUtilServlet.java:74)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at weblogic.servlet.internal.ServletStubImpl$ServletInvocationAction.run(ServletStubImpl.java:1072)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:465)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:348)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:6981)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)
at weblogic.servlet.internal.WebAppServletContext.invokeServlet(WebAppServletContext.java:3892)
at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:2766)
at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:224)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:183)
Caused by: javax.jms.JMSException: MQJMS2007: failed to send message to MQ queue
at com.ibm.mq.jms.services.ConfigEnvironment.newException(ConfigEnvironment.java:553)
at com.ibm.mq.jms.MQMessageProducer.sendInternal(MQMessageProducer.java:1589)
at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:1012)
at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:1046)
at com.bhr.infra.messaging.PMQueueSender.send(PMQueueSender.java:57)
at com.bhr.infra.messaging.PMHandler.send(PMHandler.java:410)
... 13 more
13 Oct 2006 16:47:15,486 ERROR MessagingUtilServlet [ExecuteThread: '14' for queue: 'weblogic.kernel.Default'][]: **********EXCEPTION TRACE END**************
原因分析和解决方法:
It may be caused by use of old version MQ jar. It can receive message, but cannot send message.
如果你碰到"Cannot Forward a Response that is Already Committed"这样的错误信息,一定是应用对一次http请求做了多次页面跳转或对response对象做过close后,又去写响应.
这个问题的根本原因由于响应本次请求的Response对象的状态是已提交状态造成的, 它不允许响应提交多次。
什么时候Response对象的状态变成已提交状态:当你的应用已经实现了页面跳转逻辑。
通常原因:
1. 如果你使用了community的话, community的安全认证和授权如果没有通过,community内部会进行页面跳转。但你的应用(对于workshop应用的话,指的是action)又试图进行页面跳转,就会出现这样的问题。
/**
* @jpf:action
* @jpf:forward name="page" path="page.jsp"
*/
protected Forward actionMethod()
{
//如果已经提交了响应,你仍然试图再次进行页面跳转,即再次提交响应,可能出现问题
//通常可以加一个判断
if (!getResponse().isCommitted()) {
return new Forward("success");
} else {
return null;
}
}
protected void beforeAction() {
SecurityManager.checkAuthorization(getRequest(), getResponse(), config.getString(
CDConstants.CREATE_TERM_ACTION_NAME, ""));
//这个调用可能实现了页面跳转,即提交响应
}
注意JSP中实现页面跳转后,加return 语句防止继续提交响应:
<%
SecurityManager.checkAuthorization(getRequest(), getResponse(), config.getString(
CDConstants.CREATE_TERM_ACTION_NAME, ""));
{
//可能有实现页面跳转逻辑
……………forward();
return;//记得加retrun.
}
%>
2. .如果没有使用community的话,下面情景也会出现这个问题
/**
* @jpf:action
* @jpf:forward name="page" path="page.jsp"
*/
protected Forward actionMethod()
//直接操作Response对象写响应
getResponse().getPrintWriter().println();
…
/
// 又执行的页面跳转,也可能出现问题
return new Forward("index");
}
一个页面流在第一次使用时,内部属性currentpage并没有指定页面,所以避免在begin方法里使用return-to: current page用法.同时在用的时候注意页面流的工作方式
问题描述:
Error message like below in Web Page is displayed:
Page Flow:
/com/xxxx/xxx/xxx.jpf
Action:
update
Page Flow Error - No Relevant Page
You have tried to return to a recent page in the current page flow through return-to="currentPage",
but there is no appropriate page.
Exception's stack trace:
Exception: No previous page for return-to="currentPage" on action create in page flow
/com/gems/mt/web/CreateReleaseCntr/CreateReleaseCntrController.jpf.
com.bea.wlw.netui.pageflow.NoPreviousPageException: No relevant page for return-to="currentPage"
on action create in page flow /com/gems/mt/web/CreateReleaseCntr/CreateReleaseCntrController.jpf.
at com.bea.wlw.netui.pageflow.FlowController.doReturnToPage(FlowController.java:1328)
at com.bea.wlw.netui.pageflow.FlowController.forwardTo(FlowController.java:1023)
at com.bea.wlw.netui.pageflow.PageFlowController.forwardTo(PageFlowController.java:606)
at com.bea.wlw.netui.pageflow.FlowController.internalExecute(FlowController.java:765)
...
原因分析和解决方法:
When a new page flow is created and its current page is null (it mean you visit at the fist time),
so such an exception like NoPreviousPageException occur if you use return-to="currentPage".
if a new page flow finish a successful forward, it will treat last page as current page
so that we can use current page to display error message conveniently.
Solution:
ensure page flow finish a successful forward and use current page again.
You can also use path="specificPage.jsp" instead of return-to="currentPage"
if your original return-to is just one specific page.
We have known, the ListCellRenderer interface has only one method public Component getListCellRendererComponent(), and it returns a Component. When a JList needs to compute its dimension or paint its cell item, this method will be called. So it will be called frequently, and we have a best practice, try to reuse object, avoid time-consumed computation and unnecessary operation in this method.
Recently found, there is a memory leak bug for list cell renderer in both JDK 1.4.x and JDK 5 (don’t know whether has the same bug in earlier JDK). The component returned from getListCellRendererComponent() method could not be GC, and all its referenced objects also could not be GC. Unfortunately, this bug is only fixed in JDK 6.
So currently, if a JList use the ListCellRenderer, but only holds a little resource (eg: the JList only has little items, and the component is returned from ListCellRenderer is a simple JLable), you may not care about the memory leak.
But if a JList use the ListCellRenderer, and holds a large resource, you should remember to use the following way to avoid memory leak.
Work Around: Subclass JList and invoke removeAll after painting is done:
public class xxxList extends JList {
private CellRendererPane renderer;
private CellRendererPane getRenderer() {
if (renderer == null) {
for (int i=0; i<getComponents().length; i++) {
Component c = getComponents()[i];
if (c instanceof CellRendererPane) {
renderer = (CellRendererPane)c;
break;
}
}
}
return renderer;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
CellRendererPane renderer = getRenderer();
if (renderer != null) {
renderer.removeAll();
}
}
}
Please refer to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5044798 for more details.
The logic to change state is only inside one method (initVesselView(…)) inside GrVessel.java.
if (viewType == VIEW_STOWAGE) { vslView_ = new VesselStowageView(leftPoint, bow2Left, leftCenter, rightCenter, shipWidth_); } else if (viewType == VIEW_SIDE) { vslView_ = new VesselSideView(leftPoint, bow2Left, leftCenter, rightCenter, shipWidth_); } else if (viewType == VIEW_PLAN) { vslView_ = new VesselPlanView(leftPoint, bow2Left, leftCenter, rightCenter, shipWidth_); } else { throw new IllegalArgumentException( "GrVessel::initVesselView-->Invalid view type!"); }
We do not need to repeat this logic of changing view in many other method.
This is the benefit of State design pattern.
The 3 state classes here are VesselPlanView.java, VesselSideView.java and VesselStowageView.java
Please note that in the state classes, if a method is supported by stateA and not supported by stateB.
Then, in stateB, the method body will throw an exception (IllegalArgumentException) to indicate that coder call the wrong method in the wrong state.
Example, in VesselPlanView.java, public boolean addStowage(...). This method is only meaningful to stowageView.
public boolean addStowage(IlvManager manager, List transformList, IlvGraphic stowage, String stadBayN, boolean isSelect) { throw new IllegalArgumentException( "GrVessel::addStowage-->be sure the vessel at stowage view!"); }
Hence, in PlanView, the method body will throw exception.
Just to share with you a better way to code.
摘要: 本文讨论模型过滤技术。您可将这一技术用于 Swing 组件集,这样即可在不改变底层数据的条件下提供模型数据的不同视图。过滤器可以改变数据元素的外在内容,将数据排除在视图之外、将外部元素包含进数据集中、或者以不同的顺序呈现元素。过滤器既可应用于数据模型,也可应用于状态模型。您还可以叠用过滤器,以将它们的效果组合起来。 阅读全文
有时运行ANT 时会抛出 java.lang.InstantiationException: org.apache.tools.ant.Main 异常 原因之一是在机器中存在2种不同版本的ANT,我碰到的情况是classpath 中即有weblogic.jar,又有1.6.5的ANT, 删除weblogic.jar后运行就正常了
public class GroupableHeaderExample extends JFrame {
GroupableHeaderExample() { super( "Groupable Header Example" );
DefaultTableModel dm = new DefaultTableModel(); dm.setDataVector(new Object[][]{ {"119","foo","bar","ja","ko","zh"}, {"911","bar","foo","en","fr","pt"}}, new Object[]{"SNo.","1","2","Native","2","3"});
JTable table = new JTable( dm ) { protected JTableHeader createDefaultTableHeader() { return new GroupableTableHeader(columnModel); } }; TableColumnModel cm = table.getColumnModel(); ColumnGroup g_name = new ColumnGroup("Name"); g_name.add(cm.getColumn(1)); g_name.add(cm.getColumn(2)); ColumnGroup g_lang = new ColumnGroup("Language"); g_lang.add(cm.getColumn(3)); ColumnGroup g_other = new ColumnGroup("Others"); g_other.add(cm.getColumn(4)); g_other.add(cm.getColumn(5)); g_lang.add(g_other); GroupableTableHeader header = (GroupableTableHeader)table.getTableHeader(); header.addColumnGroup(g_name); header.addColumnGroup(g_lang);
TableCellRenderer renderer = new DefaultTableCellRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JTableHeader header = table.getTableHeader(); if (header != null) { setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } setHorizontalAlignment(JLabel.CENTER); setText((value == null) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } };
TableColumnModel model = table.getColumnModel(); for (int i=0;i<model.getColumnCount();i++) { model.getColumn(i).setHeaderRenderer(renderer); } JScrollPane scroll = new JScrollPane( table ); getContentPane().add( scroll ); setSize( 400, 120 ); }
public static void main(String[] args) { GroupableHeaderExample frame = new GroupableHeaderExample(); frame.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent e ) { System.exit(0); } }); frame.setVisible(true); } }
============================================= public void paint(Graphics g, JComponent c) { Rectangle clipBounds = g.getClipBounds(); if (header.getColumnModel() == null) return; // ((GroupableTableHeader)header).setColumnMargin(); int column = 0; Dimension size = header.getSize(); Rectangle cellRect = new Rectangle(0, 0, size.width, size.height); Hashtable h = new Hashtable(); // int columnMargin = header.getColumnModel().getColumnMargin();
Enumeration enumeration = header.getColumnModel().getColumns(); while (enumeration.hasMoreElements()) { cellRect.height = size.height; cellRect.y = 0; TableColumn aColumn = (TableColumn)enumeration.nextElement(); Enumeration cGroups = ((GroupableTableHeader)header).getColumnGroups(aColumn); if (cGroups != null) { int groupHeight = 0; while (cGroups.hasMoreElements()) { ColumnGroup cGroup = (ColumnGroup)cGroups.nextElement(); Rectangle groupRect = (Rectangle)h.get(cGroup); if (groupRect == null) { groupRect = new Rectangle(cellRect); Dimension d = cGroup.getSize(header.getTable()); groupRect.width = d.width; groupRect.height = d.height; h.put(cGroup, groupRect); } paintCell(g, groupRect, cGroup); groupHeight += groupRect.height; cellRect.height = size.height - groupHeight; cellRect.y = groupHeight; } } cellRect.width = aColumn.getWidth() ;//+ columnMargin; if (cellRect.intersects(clipBounds)) { paintCell(g, cellRect, column); } cellRect.x += cellRect.width; column++; } }
使用
IlvToolTipManager
创建一个多行的
tooltip
:
1)
首先需要注册
view (IlvManagerView)I
lvToolTipManager.registerView(view);
2)
创建需要显示的信息的数组,即每行的信息为数组中的一个元素:
new String[] tooltipArray ;
3)
创建
tooltip,
第一个参数是上面创建的数组,第二个参数是显示位置,必须是
SwingConstants.LEFT
,
RIGHT
, or
CENTER
String tooltip = IlvToolTipManager.createMultiLineToolTipText(tooltipArray, SwingConstants.LEFT);
4)
设置
tooltip
:
IlvGraphic.setToolTipText(tooltip);
public static String createMultiLineToolTipText(String as[], int i) { String s; switch(i) { case 2: // '\002' s = "left"; break;
case 4: // '\004' s = "right"; break;
case 0: // '\0' s = "center"; break;
case 1: // '\001' case 3: // '\003' default: throw new IllegalArgumentException("Alignment must be LEFT, RIGHT, or CENTER"); } Font font = UIManager.getFont("ToolTip.font"); StringBuffer stringbuffer = new StringBuffer("<p align=\""); stringbuffer.append(s); stringbuffer.append("\" style=\"font-family:"); stringbuffer.append(font.getName()); stringbuffer.append(";font-size:"); stringbuffer.append(font.getSize()); stringbuffer.append("pt\">"); String s1 = stringbuffer.toString(); StringBuffer stringbuffer1 = new StringBuffer("<html>"); for(int j = 0; j < as.length; j++) { stringbuffer1.append(s1); stringbuffer1.append(as[j]); stringbuffer1.append("</p>"); }
stringbuffer1.append("</html>"); return stringbuffer1.toString(); }
下面这个异常是因为没有找到EJB, JNDI NAME 本来是 'ejb/ppp/sss/Resource' , 可找的却是'ejb.ppp.sss/Resource' , 后来查到是因为EJB 的REMOTE 接口中的方法在BEAN中没有定义. 不知道还有没有其他什么原因可以导致这种情况.
Caused by: javax.naming.NameNotFoundException: While trying to lookup 'ejb.ppp.sss/Resource' didn't find subcontext 'ppp' Resolved ejb at weblogic.jndi.internal.BasicNamingNode.newNameNotFoundException(BasicNamingNode.java:924) at weblogic.jndi.internal.BasicNamingNode.lookupHere(BasicNamingNode.java:225) at weblogic.jndi.internal.ServerNamingNode.lookupHere(ServerNamingNode.java:154) at weblogic.jndi.internal.BasicNamingNode.lookup(BasicNamingNode.java:188) at weblogic.jndi.internal.BasicNamingNode.lookup(BasicNamingNode.java:196) at weblogic.jndi.internal.RootNamingNode_WLSkel.invoke(Unknown Source) at weblogic.rmi.internal.BasicServerRef.invoke(BasicServerRef.java:492) at weblogic.rmi.cluster.ReplicaAwareServerRef.invoke(ReplicaAwareServerRef.java:108) at weblogic.rmi.internal.BasicServerRef$1.run(BasicServerRef.java:435) at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:363) at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:147) at weblogic.rmi.internal.BasicServerRef.handleRequest(BasicServerRef.java:430) at weblogic.rmi.internal.BasicExecuteRequest.execute(BasicExecuteRequest.java:35) at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:224) at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:183)
摘要: 我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?
将下列原则应用到你的软件工程中,你会获得立杆见影的成果 阅读全文
Eclipse插件下载列表 MyEclipse:J2EE开发插件,支持JSP、EJB、数据库等操作。 下载站点: www.myeclipseide.comLomboz:和MyEclipse的同类型插件且免费。 下载站点: http://forge.objectweb.org/project/showfiles.php?group_id=97XMLBuddy:xml文件编辑器 下载站点: www.xmlbuddy.comFat Jar:项目打包插件,可以将项目支持包和项目本身打成一个包。 下载站点: http://fjep.sourceforge.net/Jinto:国际化插件 下载站点: http://www.guh-software.de/Jasper Assistant:报表插件 下载站点: http://www.jasperassistant.com/Log4E Log4j插件,提供Log4j的快速操作。Log4j专用于为程序输入调试信息。 下载站点: http://log4e.jayefem.de/index.php/Main_PageVSSplugin: VSS客户端插件,VSS是一个和CVS齐名的版本管理系统。 下载站点: http://sourcefore.net/projects/vsspluginImplementors: 当追踪方法代码时,Eclipse默认是转到方法的接口类,而接口中是只有方法名称没有代码的。此插件提供了追踪到方法的实现代码功能。 下载页面: http://eclipse-tools.sourceforge.net/implementors/Call Hierarchy: 显示一个方法的调用层次,可以从中看到它被哪些方法调用了,以及它调用了哪些方法,是代码追踪比较实用的工具。 下载站点: http://eclipse-tools.sourceforge.net/ccall-hierarchy/Hebernate Synchronizer: Hibernate插件,提供Hibernate的自动映射等操作。 下载站点: http://www.binamics.com/hibernatesync/Profiler: 性能跟踪、测量工具,能跟踪、测试B/S模式开发的程序。 下载站点: http://sourceforge.net/projects/eclipsecolorer/myeclipse http://www.myeclipseide.com/ContentExpress-display-ceid-10.htmlWindowBuilder Pro - SWT/Swing Designer http://www.swt-designer.com/
WLS 9.1与MQ v5.3 通过JMS Bridge通信配置
最近参与一个系统的原型开发,原型要求演示WebLogic Server9.1与其他第三方产品的通信支持,包括与IBM MQSeries的双向交互、与Tuxedo的双向交互、支持与.NET的通过Web Service交互等。我负责完成与IBM MQSeries的双向交互这部分,在网上找了不少文章,发现其中基本都是基于WebLogic Server 8.1实现的。因为WebLogic Server 9.1在JMS上有很大的增强,所以我参考以前的文章,自己实现了WebLogic Server 9.1与IBM MQSeries 5.3的双向交互并且进行了测试。想必很多同行会遇到跟我一样的问题,特此撰文一片跟大家分享。本文配置在window xp上测试成功。
概述
目标是实现WebLogic Server 9.1和IBM MQSeries5.3之间的的双向交互,包括:
- WebLogic Server 9.1消息转发给IBM MQSeries5.3
- IBM MQSeries 5.3消息转发给WebLogic Server 9.1
具体地,将WebLogic Server9.1队列WLSSendQueue的消息转发到IBM MQSeries 5.3队列MQReceiveQueue,同时将IBM MQSeries 5.3队列MQSendQueue的消息转发到WebLogic Server 9.1队列WLSSendQueue。
WebLogic Server包含一个完整的、有丰富特性的消息服务器。第三方的消息服务器(如IBM MQSeries),只要其提供了JMS API的实现,也可以在其中运行。Messaging Bridge是一种由WebLogic Server提供的J2EE设备,用于转发两个消息提供者的消息。你可以使用Messaging Bridge将消息从一个消息提供者的目的地(队列或者主题)移至另外一个消息提供者的目的地。因此,当WebLogic应用程序需要与第三方消息提供者 (比如IBM MQSeries)进行交互时,Messaging Bridge就可以承担这个中间角色。我们需要做如下配置:
- 通过WebLogic控制台建立两个WebLogic队列:发送队列WLSSendQueue和接收队列WLSReceiveQueue。
- 类似地,通过MQ资源管理器建立两个MQ本地队列:发送队列MQSendQueue和接收队列MQReceiveQueue。
- 为 了实现消息转发需要建立两个Messaging Bridge:WLS2MQBridge 和MQ2WLSBridge 。WLS2MQBridge:将WebLogic发送队列WLSSendQueue的消息转发到MQ接收队列MQReceiveQueue; MQ2WLSBridge:将MQ发送队列MQSendQueue的消息转发到WebLogic接收队列WLSReceiveQueue。
- 为 了实现事务性消息转发, WebLogic需要使用XAQueueConnectionFactory,而MQ需要使用MQXAQueueConnectionFactory。这 就确定了WebLogic需要使用支持XA的连接工厂,MQ必须采用绑定的模式,并且WebLogic和MQ必须安装在同一台机器上。
WLS配置
WebLogic Server 9.1在WebLogic JMS的配置、部署和动态管理方面引入了重要的改进。它对JMS 1.1规范提供官方支持。此外,在系统中添加了人们期待已久的消息排序高级特性。XML API的XML消息处理功能得到了增强。在WebLogic 9.1平台上使用JMS非常轻松有趣、可靠且迅速。下面是现有新特性中的一些亮点。
- 自动化的 JMS 故障恢复
自动化的JMS故障恢复是业内期待已久的特性。JMS利用“Automatic WebLogic Server Migration”特性来提供自动化的JMS故障恢复。在整个WebLogic Server实例进行故障恢复时,JMS也将自动从故障中恢复过来。尽管其他的一些JMS服务器提供商已经利用一些复杂装置提供了这样的功能,但 WebLogic 9.1的实现是最直观而清晰的。
- 排序单元
消息排序是大多数消息处理应用程序的一项基本要求。WebLogic Server JMS即使在集群环境中也能确保消息的顺序处理。它甚至可以定义多个组来将消息分组,这样每个组都拥有自己的处理顺序(如图1所示)。
- 存储转发 (SAF)
WebLogic存储转发(store and forward, SAF)服务使WebLogic Server能在通过WebLogic Server实例部署的应用程序间可靠地交付信息。SAF的强大功能使得我们可以很容易地将多个消息服务链接在一起(如图2所示)。
- Messaging Bridge具有如下优点:
- 不需要编码,纯配置,加速你的开发;
- 灵活的体系结构,容易配置多个Messaging Bridges,并且而且可以动态的启动和停止单个Messaging Bridge;
- 采用即取即用的MQ 适配器,实现全面的MQ JMS 支持,能够设定MQ 主题查询;
- 充分利用WebLogic容器进行服务管理,并且集中所有的Bridges资源在一个线程池;
- 全面的事务处理能力,两阶段事务处理;
- 全面的JCA 支持;
- 确保服务质量和连接管理,实实在在的一次性服务;
- 控制台监视能力;
- 集成BEA WebLogic应用与外部消息提供商,以便将新的应用与现有的投资连接起来。
创建Server域
创建Server Domain, domain名称jms_domain
修改启动文件
修改WebLogic的启动文件startWebLogic.cmd。将IBM MQSeries和IBM MQSeries Java的安装目录加到WebLogic path下,同时将MQ JMS Java类包加入到WebLogic classpath下:
@rem added the following to configure messaging bridge with local MQSeries installation
set MQ_INSTALL_PATH=D:\installed\MQ
set MQ_JAVA_INSTALL_PATH=D:\installed\MQ\Java
set MQ_JAVA_LIB=%MQ_JAVA_INSTALL_PATH%\lib
set MQ_CLASSPATH=%MQ_JAVA_LIB%\com.ibm.mq.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\com.ibm.mqbind.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\com.ibm.mqjms.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\fscontext.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\jms.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\jndi.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\jta.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\ldap.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%\providerutil.jar
set MQ_PATH=%MQ_INSTALL_PATH%\in;%MQ_JAVA_INSTALL_PATH%\in;
%MQ_JAVA_INSTALL_PATH%\lib
set PATH=%MQ_PATH%;%PATH%
set CLASSPATH=%MQ_CLASSPATH%;%WEBLOGIC_CLASSPATH%;
%POINTBASE_CLASSPATH%;%JAVA_HOME%\jre\lib\rt.jar;
%WL_HOME%\server\lib\webservices.jar;%CLASSPATH%
配置JMS
注意WLS91的连接工厂和队列要到JMS Modules下面设置:
- 创建文件后备存储BridgeFileStore
- 创建JMS服务器BridgeJMSServer,和Paging Store设为" BridgeFileStore" ,Target为adminServer
- 创建支持XA的连接工厂WLSCFXA,JNDI Name为bridge.wlsCFXA
- 创建JMS发送队列WLSSendQueue,JNDI名称为bridge.wlsSendQueue,Target为BridgeJMSServer
- 创建JMS接收队列WLSReceiveQueue,JNDI名称为bridge.wlsReceiveQueue,Target为BridgeJMSServer
配置Messaging Bridge Destination
- JMS Bridge Destination名称为MQReceiveBridgeDestination
- Adapter JNDI Name: eis.jms.WLSConnectionFactoryJNDIXA
- Connection URL为file:/D:/installed/MQ/Queues,
- Initial Context Factory为com.sun.jndi.fscontext.RefFSContextFactory,
- Connection Factory JNDI Name为bridge.mqQCFXA,
- Destination JNDI Name为bridge.mqReceiveQueue
- Destination Type: Queue
- JMS Bridge Destination名称为MQSendBridgeDestination
- Adapter JNDI Name: eis.jms.WLSConnectionFactoryJNDIXA
- Connection URL为file:/D:/installed/MQ/Queues,
- Initial Context Factory为com.sun.jndi.fscontext.RefFSContextFactory,
- Connection Factory JNDI Name为bridge.mqQCFXA,
- Destination JNDI Name为bridge.mqSendQueue
- Destination Type: Queue
- JMS Bridge Destination名称为WLSReceiveBridgeDestination
- Adapter JNDI Name: eis.jms.WLSConnectionFactoryJNDIXA
- Connection URL为t3://localhost:7001,
- Initial Context Factory为weblogic.jndi.WLInitialContextFactory,
- Connection Factory JNDI Name为bridge.wlsCFXA,
- Destination JNDI Name为bridge.wlsReceiveQueue
- Destination Type: Queue
- User Name: weblogic(域配置时指定的)
- User Password: weblogic(域配置时指定的)
- JMS Bridge Destination名称为WLSSendBridgeDestination
- Adapter JNDI Name: eis.jms.WLSConnectionFactoryJNDIXA
- Connection URL为t3://localhost:7001,
- Initial Context Factory为weblogic.jndi.WLInitialContextFactory,
- Connection Factory JNDI Name为bridge.wlsCFXA,
- Destination JNDI Name为bridge.wlsSendQueue,
- Destination Type: Queue
- User Name: weblogic(域配置时指定的)
- User Password: weblogic(域配置时指定的)
配置Messaging Bridge
- JMS Bridge名称为MQ2WLSBridge
- Source Bridge Destination: MQSendBridgeDestination
- Target Bridge Destination: WLSReceiveBridgeDestination
- Quality Of Service: Exactly-Once
- Started: Yes
- JMS Bridge名称为WLS2MQBridge
- Source Bridge Destination: WLSSendBridgeDestination
- Target Bridge Destination: MQReceiveBridgeDestination
- Quality Of Service: Exactly-Once
- Started: Yes
MQ 配置
IBM MQSeries是IBM的商业通讯中间件(Commercial Messaging Middleware)。IBM MQSeries提供一个具有工业标准,安全,可靠的信息传输系统。它的功能是控制和管理一个集成的商业应用,使得组成这个商业应用的多个分支程序(模块)之间通过传递信息完成整个工作流程。IBM MQSeries具有特殊的技术防止信息重复传送,确保信息一次且仅一次(once-and-only-once)传递,保证传输的可靠性。本文使用的 MQ版本为IBM MQSeries 5.3。
IBM MQSeries基本由一个消息传输系统和一个应用程序接口组成,其资源是消息和队列(Messaging and Queuing)。
队列管理器(Queue Manager):管理队列的系统,实现网络通信,保证消息安全可靠地传输到目的地。用于确保队列之间的信息提供,包括网络中不同系统上的的远程队列之间的信息提供。并保证网络故障或关闭后的恢复。
队列:一个安全的信息存储区。因为信息存放在队列中,所以应用程序可以相互独立的运行,以不同的速度,在不同的时间,在不同的地点。
本地队列:对程序而言,本地队列属于该程序所连接的队列管理器。
远程队列:该队列不属于该程序所连接的队列管理器,而只是远端队列管理器的队列在本地的定义。
传输队列:它是一个本地队列,保存了指定要发送到远端的消息。
死信队列:它是一个本地队列,用于存放无法传递的消息。
通道:在两个队列管理器之间建立起来的数据传输链路。
应用程序接口:应用程序和信息系统之间通过MQSeries API实现的接口。
Install MQ
- 安装过程中选择自定义安装模式,并确保安装JMS所需的Java jar包支持如下图:
(缺省安装未包含)
确认MQ服务已启动。
可通过MicroSoft windows控制面板中管理工具下的服务控制台确认。如下图:
从程序菜单启动MQ 资源管理器
如果程序提示试用版过期,可以通过修改系统时间搞定.我就是修改到了2004年。
通过MQ资源管理器创建一个通道
建立名为BRIDGE.CHANNEL的通道,其他参数缺省设置。
通过MQ资源管理器创建两个本地队列
建立两个本地队名为MQReceiveQueue, MQSendQueue,其他参数缺省设置。
更新MQ安装目录下与JMS配置相关的文件
JMSAdmin.config文件位于%MQ_INSTALLL_HOME_PATH%\Java\bin,主要定义JNDI服务的提供商,即JMS Server Factory和URL。%MQ_INSTALLL_HOME_PATH%\Java\bin目录下新建目录bridgeconfig,将JMSAdmin.config文件拷贝到bridgeconfig。
使用Sun的文件JNDI服务Factory,故定义
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
PROVIDER_URL=file:/D:/installed/MQ/Queues
SECURITY_AUTHENTICATION=none
(例子中,MQ安装目录%MQ_INSTALLL_HOME_PATH%为:D:/installed/MQ/)
JMSAdmin.bat文件也位于%MQ_INSTALLL_HOME_PATH%\Java\bin,此文件用于启动MQ的JMS命令行管理界面,要设置启动JMS命令行管理界面所需环境变量。在JMSAdmin.bat中添加如下环境变量:
set MQ_JAVA_INSTALL_PATH=D:\installed\MQ\Java
set MQ_JAVA_LIB=%MQ_JAVA_INSTALL_PATH%lib
set MQ_CLASSPATH=%MQ_JAVA_LIB%com.ibm.mq.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%com.ibm.mqbind.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%com.ibm.mqjms.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%fscontext.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%jms.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%jta.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%ldap.jar
set MQ_CLASSPATH=%MQ_CLASSPATH%;%MQ_JAVA_LIB%providerutil.jar
进入MQ的JMS命令行管理界面绑定MQ的JMS Factory以及JMS本地队列
- 如上图所示界面,生成一个MQXAQueueConnectionFactory对象,Messaging Bridge将使用这个工厂对象建立MQ的XA连接,使用命令DEFINE XAQCF,起名:bridge.mqQCFXA。
- 生成JMSQueue对象来绑定MQ队列MQReceiveQueue。
- 生成JMSQueue对象来绑定MQ队列MQSendQueue。
- 使用dis ctx命令,查看目前已有的对象和绑定,可以看到XA连接工厂和队列都已绑定。
测试
MQ发送,WLS接收
- MQ发送队列放入测试消息
- 在WLS控制台监测消息到达
WLS发送,MQ接收
- WLS发送队列放入测试消息
执行下面的代码,给WLS发送队列放入测试消息 package com.bea;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.jms.*;
import java.util.Hashtable;
/**
* Created by IntelliJ IDEA.
* User: pmeng
* Date: 2006-6-3
* Time: 14:54:08
*/
public class SendMessageTest {
public static void main(String[] args) {
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://localhost:7001");
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("bridge.wlsCFXA");
QueueConnection connection = factory.createQueueConnection();
QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup("bridge.wlsSendQueue");
connection.start();
QueueSender queueSender = session.createSender(queue);
TextMessage msg = session.createTextMessage();
msg.setText("Menghe.");
queueSender.send(msg);
}
catch (NamingException e) {
System.out.println("NamingException:" + e.getMessage());
e.printStackTrace();
}
catch (JMSException e) {
System.out.println("JMSException:" + e.getMessage());
e.printStackTrace();
}
}
}
- MQ资源管理器查看接收到的消息
参考资料
- BEA WebLogic Server 8.1 JMS与 IBM MQSeries 集成方案,周海根,
http://dev2dev.bea.com.cn/techdoc/20040411784.html
- BEA WebLogic 8.1 JMS 与IBM MQ v5.3 通过JMS Message Bridge通信配置,陈仁祥
http://e-docs.bea.com/wls/docs91/messaging.html
转自http://www.lrsolution.com/docs/MQvsWLJMS.html
比较IBM MQSeries和BEA WebLogic JMS Server
刘睿 2005年7月
在面向消息的中间件(MOM)这个领域,IBM MQSeries (又称WebSphere MQ)一直是当仁不让的超级大哥,其它还有一些小兄弟,比如SwiftMQ、SonicMQ之类。但近年来随着J2EE中的JMS规范的建立,完备地支持JMS的服务器如雨后春笋般地出现,比如BEA WebLogic Server的JMS Server就是其中一个佼佼者。
仅仅就JMS规范来说,MQSeries与WebLogic JMS没有什么不同之处。但JMS规范仅仅定义了消息服务器的一个开发接口,而且还忽略了许多细节,所以不同之处就在JMS规范之外的这些内容,很多也是非常重要的。总的来说,MQSeries的功能和性能方面明显占优,而WebLogic JMS的某些JMS配置更加简单易行。
在本文中,我尽量试图从客观的角度分析两种产品的差异,如有不妥之处,请读者不吝赐教。
1. 产品体系架构不同造成的差异
WebLogic JMS是一个纯Java实现的支持C-S架构的实现JMS规范的服务器产品;而MQSeries是使用本地语言(比如在UNIX和Windows上的C语言)编写的既支持C-S架构,又支持对等访问的实现完备MOM(包括JMS规范)的产品。于是就产生出以下的不同点:
1.1 MQSeries支持真正的异步数据传输;而WebLogic JMS不支持。
异步发送数据到远端的消息服务器,是MQSeries等完备的MOM的特色。JMS规范规定了一个C-S架构,定义了JMS客户机与JMS服务器的开发接口,并没有定义JMS服务器与JMS服务器的规范,而客户机方面没有任何队列,所以只能说是规范了消息的存取,而没有规范消息数据的传输。因为JMS客户机并不拥有存放数据的队列,所以所有发送的操作都要由应用程序来控制,JMS服务器本身并不代理传输,也不保证数据在远程队列间传输的可靠性。WebLogic JMS就是这样的体系。
这种体系结构有时候是不能直接满足应用的要求的。首先,为了充分利用资源和提高效率,许多应用需要采用异步消息的机制。其次,许多需要快速返回的应用也必须使用异步传输。比如电话自动语音应答(IVR)的程序,某个操作需要把数据传输到远程的服务器上,但是必须立即返回,接受客户的下一个按键。
MQSeries通过通道与传输队列和远程队列来完成这一任务。能充分利用网络的带宽,甚至支持断网续传,保证数据传输的可靠性。当然,虽然应用程序不必作任何工作,但配置方面确实还要多学一些概念。
1.2 MQSeries支持多种语言的开发;而WebLogic JMS基本上只支持JAVA
MQSeries支持的语言包括C, C++, COBOL, JAVA, PL/1, REXX, RPG, Visual Basic (使用COM/ActiveX)等。老板本的MQSeries支持JAVA是通过一个叫MA88的SupportPac来实现,虽然经过广泛的使用和验证,但给人的感觉是不太方便。好在从5.3版起(目前最新的是6.0版),JAVA支持已经内置在MQSeries中。
WebLogic JMS一般只支持JAVA开发。但BEA也在dev2dev.bea.com网站上提供了一套免费的C的支持,称作“JMS C API”。参见http://dev2dev.bea.com/utilitiestools/environment.html?highlight=utilitiestools。但这个工具与老的MA88也是不能相提并论的,因为BEA并不真正支持它,因此也基本没有什么用户。参见BEA网站上关于“JMS C API”的警告:
1.3 纯JAVA实现的利与弊
MQSeries是用本地语言实现的,因此带来的好处是高性能和高并发的支持能力。MQSeries相对WebLogic JMS等产品的性能优势是非常明显的,所以MQSeries非常适于企业级的大数据量和高并发的数据传输业务。谁也无法想象一个企业级的数据应用会采用一个纯Java实现的数据库,因为其性能无法满足要求,对较大的数据传输应用也是一样的,纯Java实现的JMS服务器例如WebLogic JMS无法满足其性能的要求。
纯JAVA实现的JMS服务器也有其好处,就是与其它的J2EE服务完美地集成在一起。所以WebLogic的JMS配置显得更简洁。WebSphere+MQSeries也配合得很好,但总是能感觉到是这两个产品。WebLogic JMS的对象体系完全符合JMS的概念体系。而MQSeries要通过WebSphere Application Server或者一个叫JMSAdmin的工具,借助于目录服务来完成MQSeries概念体系到JMS概念体系的映射。应该是看到了这件事造成的麻烦,所以IBM在WebSphere v6也提供了一套纯JAVA实现的、与MQSeries可以互操作的JMS服务器。另外一点是WebLogic不需要WebSphere以及MQSeries那样的冗长的CLASSPATH等环境变量的设置,这点对开发人员有吸引力。
1.4 MQSeries的通信功能更加强大,WebLogic JMS也有自己的一些特色
JMS对通信功能的要求很少,所以对二者对通信支持能力还是有很大的差别的。总的来说,历史更悠久的MQSeries占优,但WebLogic JMS也有自己的特色。
- MQSeries支持支持真正的远程异步数据传输,甚至支持消息的路由,可以“多级跳”;WebLogic JMS不支持。
- MQSeries支持消息的分组和分段传输,对于大消息传输和不稳定的网络非常有意义。WebLogic JMS没有这方面的功能。
- 二者都支持SSL、持久性、优先级、超时等功能。除了完备的SSL实现之外,MQSeries的安全体系 遭到了一些批评,使用通道的安全出口程序显得很麻烦,而使用用户名称但无须口令保护的远程数据通信,如何能令人满意?但在这一点上WebLogic JMS也很难说就好一些,因为WebLogic JMS仅仅支持C-S的操作,系统本身并不支持远程的数据传输(需要应用实现)。
- WebLogic JMS支持IP多播会话,能显著地提高 局域网内广播通信的性能,但这种方式不保证数据接收的可靠性,只适于某些特定的应用。MQSeries本身不提供此功能,但在Event Broker和Message Broker等MQSeries的升级产品中提供IP多播的支持。
1.5 MQSeries的管理功能更加强大
JMS对管理功能的要求很少,在这方面MQSeries也有比较明显的优势。
- JMS对事务处理的支持包括的对XASession和XAConnection实现,这一点对MQSeries和WebLogic JMS是相同的。MQSeries本身还可以作为事务管理器,协调两阶段提交。
- MQSeries和WebLogic JMS都支持Message Driven Bean作为触发新的应用的一种方式。WebLogic JMS还支持一种称作Session Pool的类似的触发机制。但这类触发机制过于简化,也就是每个消息都触发一个新的线程的应用。MQSeries的触发机制更丰富,不但包括这种被称作Every的方式,还包括First和Depth等方式。另外MQSeries还可以触发各种执行程序或者MQSeries的通道。
- MQSeries拥有一套完备的日志系统,可以进行独立的系统备份和恢复,因此适于高规格的数据/消息传输的应用。WebLogic JMS没有这方面的支持。
2. 产品历史的不同造成的差异
MQSeries是个历史悠久的产品,而WebLogic JMS是个新兵,因此会有以下的差异:
2.1 MQSeries支持更多的系统平台
支持30多种系统平台。当然值得注意的是某些平台的MQSeries是由合作伙伴实现的。
WebLogic JMS是个新产品,支持的平台数与WebLogic Server一样,只有常用的几个。有人说所有支持JDK的平台都能跑WebLogic JMS的客户机,这是不正确的说法。因为JMS是J2EE规范的一种,J2SE的SDK并不包括JMS的支持,更不要说支持WebLogic的J2EE了。
2.2 MQSeries支持更多的
通信协议
MQSeries支持很多通信协议,但目前在实践中常用的是TCP/IP协议和SNA协议。
WebLogic JMS仅支持TCP/IP协议。
有些人对MQSeries的单向通道的概念提出了异议,认为增加了配置的复杂性,仅仅是SNA协议的需要,而不是TCP/IP协议的需要。我个人认为这点也不无一些道理。但是在有防火墙的TCP/IP网络上,不同的方向还是有差异的。
3. 群集实现的差异
MQSeries与WebLogic JMS在支持群集时,差异比较大,应该说各有各的特点。
- MQSeries的群集建立在配置库和群集通道基础之上,可以定义“共享队列”;WebLogic JMS的群集建立在WebLogic群集基础之上,可以定义“分布式队列”。
- MQSeries在写共享队列时,如果发现本地有,就只写本地的队列(这称作本地优先);如果本地没有,就会轮流写到所有定义了此共享队列的队列管理器。MQSeries在读共享队列时,只能从本地取。WebLogic JMS在读写分布式队列时,不受本地影响,总是进行轮流或权重选择。听起来似乎WebLogic JMS的群集更灵活,其实也不尽然。当取得了JMS的对象QueueSender或QueueReceiver后,WebLogic实际上已经绑定了一个JMS服务器的实例。如果每次写或读一个消息,都重新生成QueueSender或QueueReceiver,虽然比较好地支持了负载均衡,但势必造成很大的性能损失。而MQSeries在轮流写共享队列时,没有这方面的问题。
- WebLogic JMS的分布式队列有一个叫做Forward Delay的有意思的属性,定义了一个时间的长度。系统一旦发现超过这个时间长度,还没有人读这个队列,就把它的消息转送给群集中有消费者的队列。有了这个属性,应用程序就可以只从一个JMS服务器的实例读消息了。
项目中需要使用IloSolver,但没人懂,网上资料太少了,不知道谁有这方面的资料给发一份,谢谢了! rcashellster@gmail.com
有两类线程:用户线程和守护线程。用户线程是那些完成有用工作的线程。 守护线程是那些仅提供辅助功能的线程。Thread 类提供了 setDaemon() 函数。Java 程序将运行到所有用户线程终止,然后它将破坏所有的守护线程。在 Java 虚拟机 (JVM) 中,即使在 main 结束以后,如果另一个用户线程仍在运行,则程序仍然可以继续运行。
摘要: 做任何事情都要仔细,出现错误要从最基本的地方着手,不能完全相信主观的判断,实际结果才是最有说服力的。往往问题都是由于很小的原因引起,却高估它,好点的只是耽误点时间,坏的就可能产生预想不到的后果。。。 阅读全文
摘要: 这里列出了大多数的船公司
ABC ABC货柜航运公司 A.B.C CONTAINTER LINE
ACBL 美国商业驳船航运公司 American Commercial Barge Line company
ACGR 冠航集团 Ace Group
ACL 大西洋集装箱航运公司 Atlantic Contaier Line
ACT 联合集装箱运输公司(英) Associated Container Transportation
。。。。。。。 阅读全文
摘要: 主要船公司名称及网址
澳大利亚国家航运有限公司-ANL(澳大利亚)
ANL CONTAINER LINE PTY Ltd
http://www.anl.com.au/index.php3 阅读全文
摘要: 备案,以便查看
随着计算机网络和分布式应用的不断发展,远程消息传递越来越成为应用系统中不可缺少的组成部分。商业消息中间件的出现保证了消息传输的可靠性,高效率和安全性,同时也减少了系统的开发周期。目前应用最多的消息中间件产品为IBM MQSeries。本文就针对MQ的基本操作与配置进行详细的阐述,希望对读者有所帮助。 阅读全文
摘要: 备案,以便随时查看
关系数据库设计之时是要遵守一定的规则的。尤其是数据库设计范式 现简单介绍1NF(第一范式),2NF(第二范式),3NF(第三范式)和BCNF,另有第四范式和第五范式留到以后再介绍。 在你设计数据库之时,若能符合这几个范式,你就是数据库设计的高手。 阅读全文
对于大型的WEB APP多数需要在集群环境下运行,那么在开发WEB APP时就需要注意几点,以便可以支持FAILOVER.
1. SESSION中的数据做了修改后一定要手动的SET到SESSION 中 如果当前的MANAGED SERVER DOWN掉,ADMIN SERVER会自动failover到其他的MANAGED SERVER, 并复制SESSION数据.当你对SESSION中的数据做了修改后,必须SET回SESSION,否则修改后的数据不会被复制到另一个SERVER,数据丢失.
2. SESSION中的数据必须是可序列化的. 很容易理解, 只有可以被序列化的数据才能被复制
3. 静态数据必须是只读的 同上, 当FAILOVER时,静态数据是不会被复制的,如果静态数据被修改了,白改! 例如经常会用静态属性去做记数器,这样就需要考虑用其他方法了
其实就算WEB APP目前没有准备使用集群,也应该注意这几点,以便以后可以方便的移植到集群.
欢迎大家指正和补充!
多数项目是由用户提出需求,我们的项目用户要求我们来推荐需求,然后由他们来审核. 今天带着1个多月整理的需求文档跟用户一起讨论,说是讨论,大部分是由我们讲解给用户听,只要有拿不定主意的地方就是一句话"改成可配置". 可配置不但可以提高系统的灵活性,也可以解决一些摸棱两可的需求.以后系统只要遇到系统可以配置的地方,不用考虑,作成可配置,一准没错!
|