2011年12月28日星期三

Tips_014:使用Total Commander 查找Word文档内容

Word文档是二进制的,一般的搜索软件无法查找其内容。我又不想装Google Desktop之类的搜索软件——功能虽然强大,但比较占地方和耗资源。
随着Word文档越来越多,找资料实在不方便:因为我只知道某个技术说明写在了某个文档中,但是一个一个点开看,实在是太费时间了。
好在我最终找到了Total Commander,感谢TC把我从一堆Word文档中解救了出来。

使用方法如下:
1.打开TC,点击“望远镜”图标

2.设置搜索条件
别的设置都一目了然,选中“Find Text”,输入你要查询的关键字。
一定要选中"Unicode",这样才能查二进制文件。

嘿嘿,可以看出,不仅仅Word文档可以被查找出来,PPT文档也可以。
Total Commander真是强大啊。

Tools_015:使用After Effects剪切视频

在线研讨会中经常需要演示Demo,为了确保不出差错,一般使用事先录制好的视频。
除了自己录制的视频之外,还有其他人录制好的视频。为了让这些视频符合自己的需要,需要裁剪成合适的长度。
这里使用Adobe公司的After Effects来剪切视频。
AE是直接处理视频编码,不是重新录屏,因此剪切后的视频质量和原始视频基本一样。
After Effects 版本:CS4。

重要步骤说明:
1. 把视频拖放到AE左边中间的Panel中

2. 把左边中间的视频拖放到下边的Panel中

3. 在下边的Panel中,点击“最佳设置”
在弹出的界面中设置参数:帧速率、开始和结束时间。

注意,我这里遇到一个问题:开始时间不知为何总是为40秒,所以我把开始和结束时间都顺延40秒后,再剪切。

4. 在下边的Panel中,点击“无损”
在弹出的界面中设置参数:压缩编码、裁剪。
其中压缩编码建议选择:Xvid MPEG-4 Codec。如果没有该选项,请先安装另一个软件:完美解码。

如果画面需要裁剪,请按下主窗口中的“目标兴趣范围”,然后选择你感兴趣的范围。

选择“裁剪”,选择“使用目标兴趣范围”,可以看出下边被剪掉了88像素。

如果不需要音频的话,就不用选“音频输出”。

5. 点击下边的Panel中最右边的按钮:渲染
好了,剩下的工作就交给AE吧,去泡杯茶,休息一下吧。

最后,感谢马达向我推荐AE,并不厌其烦地电话指导我剪切视频。

ADF_110:使用ADF Mobile开发手机Web应用

运行环境:JDeveloper 11.1.2.1.0 + Oracle Database 10g Express Edition 10.2.0.1 + Safari 5.1.2。

使用ADF Mobile开发手机Web应用与开发ADF Web应用过程类似又有不同。

重要步骤说明:

1. 创建应用
这里仍然选择Fusion Web Application(ADF)

给出应用的名称,点击finish按钮。

2. 删除ViewController Project
这个Project是开发ADF Web应用的,将其删除,选择彻底删除所有文件。

3. 在Model Project中使用ADF-BC建立模型层
连接HR Schema。

选择Department、Employee和Job表生成ADF-BC对象。

4. 新建一个Project: ViewController
注意,这里要选择Custom Project。

技术范围选择ADF Mobile Browser,其它会自动选择过来。

5. 新建一个页面: browse.jsf
注意,这里一定要勾上"Render in Mobile Device"。

页面创建后,还会增加一些新文件。

其中trinidad-config.xml表明当前使用的皮肤是哪一个,其内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
<skin-family>#{requestContext.agent.type == 'desktop'? 'richmobile': 'mobile'}</skin-family>
</trinidad-config>

其中trinidad-skins.xml包含所有的皮肤定义,其内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- To use mobile skin families in your app, please update trinidad-config.xml with below tags -->
<!-- <skin-family>#{requestContext.agent.type == 'desktop'?'richmobile': 'mobile'}</skin-family> -->
<skins xmlns="http://myfaces.apache.org/trinidad/skin">
<skin>
<id>richmobile</id>
<family>richmobile</family>
<render-kit-id>org.apache.myfaces.trinidad.desktop</render-kit-id>
<style-sheet-name>styles/richmobile.css</style-sheet-name>
</skin>
<skin>
<id>mobile</id>
<family>mobile</family>
<render-kit-id>org.apache.myfaces.trinidad.pda</render-kit-id>
<style-sheet-name>styles/mobile.css</style-sheet-name>
</skin>
</skins>

6. 向页面增加组件
注意,这里必须选择Trinidad组件,不可以选择ADF组件,因为ADF组件目前无法运行在所有的手机浏览器上。

拖放Employee Data Control生成Trinidad ReadOnly Table。

为组件设置合适的StyleClass,这一步需要参考《Oracle ADFMobile – Skinning Guide》。
也就是说,ADF Mobile是通过设置StyleClass来实现兼容不同手机浏览器。

7. 运行
这是在IE上运行的效果,不是很好看。
但是我们惊喜的发现,它实现了自动分页的功能。

这是在Safari上运行的效果,看起来是不是有些像客户端应用?
Safari可以模拟手机上浏览器的运行效果。

这里有一个小问题,就是对panelHeader的styleClass="af_m_toolbar"设置,效果不是很理想。
不知道是不是一个bug,有待求证。

当然,你可以安装不同手机的模拟器,看看在手机上运行的真实效果。

Project 下载: MobileBrowserHR.7z

2011年12月22日星期四

WLS_079:WebLogic Server高级管理之八:JMS 存储与转发

运行环境:WebLogic Server 12.1.1 开发版 + Oracle Database 10g Express Edition 10.2.0.1。

我们知道,部署在某个WebLogic Server上的应用可以通过JNDI访问到部署在该Server上的Queue/Topic。
如果有两个应用是分别部署在同一个Domain的两个Server上,或者是两个Domain的Server上,那么如何通过Queue/Topic进行通讯?
WebLogic Server中的JMS功能提供了一个Store and Forward特性,可以帮助我们实现该功能。

设计图如下:


重点步骤说明:
1. 创建两个Domain:dev_domain和dev2_domain
其中dev_domain作为消息源,dev2_domain作为消息目的。

2. 在dev2_domain创建JMS资源
JMS Modules:msg2JMSSystemResource
JMS Server:msg2JMSServer
Connection Factory:msg2ConnectionFactory
Queue:msg2Queue
Subdeployment:msg2Subdeployment


3. 在dev_domain创建JMS资源
创建完成后的JMS Modules:MySAFSystemModule。


(1)创建Store-and-Forward Agents:MySAFAgent,target到Server上。

(2)创建JMS Modules:MySAFSystemModule

(3)在MySAFSystemModule中创建SubDeployment:MySAFSubDeployment,target到MySAFAgent上。
注意,与一般的SubDeployment定义不同,这里要把target到MySAFAgent上。
不要把MySAFSubDeploymenttarget到JMS Server上,这一步很关键,不要搞错。

(4)在MySAFSystemModule中创建Connection Factory:MySAFConnectionFactory,target到MySAFSubDeployment上。

(5)在MySAFSystemModule中创建remote SAF contex:MyRemoteSAFContext。
这里指向dev2_domain。


(6)在MySAFSystemModule中创建SAF imported destinations:MySAFImportedDestination。
此步特别容易出错,建议如图配置后,直接点击finish按钮;然后再为其设置SubDeployment,选择MySAFSubDeployment。


(7)在MySAFImportedDestination中创建Queue:MySAFQueue。
注意别忘了设置Local JNDI Name。
此步也特别容易出错,如图配置后,点击save按钮时如果报出如下错误:
“The imported destination msg2Queue has been targeted to an invalid target:msgJMSServer”
请把MySAFImportedDestination的SubDeployment设置为None。
然后再重新创建Queue并保存。
最后把MySAFImportedDestination的SubDeployment再改回MySAFSubDeployment。


(8)重新启动dev_domain中的AdminServer。
查看JNDI Tree,如果能看到SAF_msg2Queue,说明SAF配置成功。


4. 测试

4.1 测试Forward
(1)分别启动dev_domain和dev2_domain中的AdminServer。
(2)向dev_domain中的SAF Queue:和SAF_msg2Queue中发消息。
(3)观察dev_domain中的SAF Queue:

点击Remote Endpoints进去查看,发现没有一条消息,消息可能已经转发走了。

(4)观察dev2_domain中的Queue:msg2Queue
发现消息都转发过来了。

4.2 测试Store and Forward
(1)只启动dev_domain中的AdminServer。
(2)向dev_domain中的SAF Queue:和SAF_msg2Queue中发消息。
(3)观察dev_domain中的SAF Queue

点击Remote Endpoints进去查看,发现消息都在这里,消息的详细信息如下。

(4)启动dev2_domain中的AdminServer
(5)观察dev2_domain中的Queue:msg2Queue
发现消息都转发过来了。
(6)再次观察dev_domain中的SAF Queue。
发现消息都没有了,已经转发走了。

说明:发送消息的代码可以参考《为JMS消息设置顺序》中的QueueSend.java。

2011年12月21日星期三

WLS_078:WebLogic Server高级管理之七:JMS Topic 持久化订阅

运行环境:WebLogic Server 12.1.1 开发版 + Oracle Database 10g Express Edition 10.2.0.1。

默认情况下,如果Topic的订阅者“不在线”,那么是无法收到Topic中的消息的。
这与一些实际情况不相符合:我订阅的杂志,我不在家,就不给我了吗?
为此,WebLogic Server提供了一个Topic持久化订阅功能。
它可以帮助我们在“离线”的情况下,上线后收到之前所有没有收到的消息。

重要步骤说明:
1. 实验准备:创建和配置JMS资源
请参考实验《为JMS消息设置过期时间》。

2. 创建File Store
Topic中的消息要想做到持久化,必须设定持久化方式,这里我们使用File Store作为Persistent Store。


3. 为JMS Server设置Persistent Store


4. 为Topic 创建Durable Subscriber
点击Topic,选择Monitor-->Durable Subscribers-->New
设置Subscription Name:ChinaSubscripiton
设置Client ID:ChinaClient


单凭Client ID还不足以唯一标识一个持久化订阅者,因为一个Client ID还可以该Topic中的多种“杂志”。
Subscription Name就相当于杂志的名称。这跟一个身份证可以订多个房间的道理类似。

5. 编写Topic 发送方代码:TopicSend.java
public void init(Context ctx, String topicName) throws NamingException, JMSException {
tconFactory = (TopicConnectionFactory)ctx.lookup(JMS_FACTORY);
tcon = tconFactory.createTopicConnection();
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic)ctx.lookup(topicName);
tpublisher = tsession.createPublisher(topic);
msg = tsession.createTextMessage();
tcon.start();
}

private static void readAndSend(TopicSend ts) throws IOException, JMSException {
BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
String line = null;
boolean quitNow = false;
do {
System.out.print("Enter message (\"quit\" to quit): \n");
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
ts.send(line);
System.out.println("JMS Message Sent: " + line + "\n");
quitNow = line.equalsIgnoreCase("quit");
}
} while (!quitNow);
}
以上代码跟发送到普通的Topic代码没有什么不同。
区别是在Topic接收方代码。

6. 编写普通的Topic 接收方代码:TopicReceiveClient.java
public void init(Context ctx, String topicName) throws NamingException, JMSException {
tconFactory = (TopicConnectionFactory)ctx.lookup(JMS_FACTORY);
tcon = tconFactory.createTopicConnection();
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic)ctx.lookup(topicName);
tsubscriber = tsession.createSubscriber(topic);
tsubscriber.setMessageListener(this);
tcon.start();
}

public void onMessage(Message msg) {
try {
String msgText;
if (msg instanceof TextMessage) {
msgText = ((TextMessage)msg).getText();
} else {
msgText = msg.toString();
}

System.out.println("Message Received: " + msgText);

if (msgText.equalsIgnoreCase("quit")) {
synchronized (this) {
quit = true;
this.notifyAll(); // Notify main thread to quit
}
}
} catch (JMSException jmse) {
System.err.println("An exception occurred: " + jmse.getMessage());
}
}

7. 编写持久化的Topic 接收方代码:DurableTopicReceiveClient.java
public void init(Context ctx, String topicName) throws NamingException, JMSException {
tconFactory = (TopicConnectionFactory)ctx.lookup(JMS_FACTORY);
tcon = tconFactory.createTopicConnection();
tcon.setClientID("ChinaClient");
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic)ctx.lookup(topicName);
tsubscriber = tsession.createDurableSubscriber(topic,"ChinaSubscription");
tsubscriber.setMessageListener(this);
tcon.start();
}

onMessage方法与普通的Topic 接收方代码相同。

8. 测试

8.1 测试环境设置
打开4个DOS命令窗口,均设置如下环境变量
set JAVA_HOME=C:\Oracle\Java\jdk1.6.0_25
set MW_HOME=C:\Oracle\wls1211_dev
set JAVA_VENDOR=Sun
运行脚本:%MW_HOME%\wlserver\server\bin\setWLSEnv.cmd

8.2 测试普通Topic订阅者特性:一对多的消息分发
(1)在DOS命令窗口中启动AdminServer。
(2)在DOS命令窗口中启动消息生产者:java TopicSend t3://localhost:7001
(3)在两个DOS命令窗口中分别启动两个消息消费者:java TopicReceiveClient t3://localhost:7001
(4)在消息生产者DOS窗口连续发送4条消息:msg1、msg2、msg3、msg4。
(5)观察两个消息消费者DOS窗口,发现两个窗口都“消费”了所有消息。
(6)在消息生产者DOS命令窗口输入quit,两个消息消费者DOS窗口都收到该消息,并退出。

8.3 测试普通Topic订阅者特性:离线后再上线,无法收到以前的消息
(1)在DOS命令窗口中启动AdminServer。
(2)在DOS命令窗口中启动消息生产者:java TopicSend t3://localhost:7001
(3)在1个DOS命令窗口中启动1个消息消费者:java TopicReceiveClient t3://localhost:7001
(4)在消息生产者DOS窗口连续发送2条消息:msg1、msg2。
(5)在1个DOS命令窗口中启动另外1个消息消费者:java TopicReceiveClient t3://localhost:7001
(6)在消息生产者DOS窗口连续发送2条消息:msg3、msg4。
(7)观察两个消息消费者DOS窗口,发现第1个窗口“消费”了所有消息,第2个窗口只“消费”了msg3、msg4。
(8)在消息生产者DOS命令窗口输入quit,两个消息消费者DOS窗口都收到该消息,并退出。

8.4 测试持久化Topic订阅者特性:离线后再上线,依然可以收到以前的消息
(1)在DOS命令窗口中启动AdminServer。
(2)在DOS命令窗口中启动消息生产者:java TopicSend t3://localhost:7001
(3)在1个DOS命令窗口中启动1个消息消费者:java TopicReceiveClient t3://localhost:7001
(4)在消息生产者DOS窗口连续发送2条消息:msg1、msg2。
(5)在1个DOS命令窗口中启动另外1个持久化消息消费者:java DurableTopicReceiveClient t3://localhost:7001
(6)在消息生产者DOS窗口连续发送2条消息:msg3、msg4。
(7)观察两个消息消费者DOS窗口,发现两个窗口都“消费”了所有消息,这说明持久化配置起作用了。
(8)在消息生产者DOS命令窗口输入quit,两个消息消费者DOS窗口都收到该消息,并退出。

8.5 测试持久化Topic订阅者特性:设置多个Subscription Name
(1)在DOS命令窗口中启动AdminServer。
(2)在DOS命令窗口中启动消息生产者:java TopicSend t3://localhost:7001
(3)在两个DOS命令窗口中分别启动两个消息消费者:java DurableTopicReceiveClient t3://localhost:7001
这时,在第2个消费者窗口会报错:说Client id:ChinaClient已经被另一个对象使用了。
Exception in thread "main" weblogic.jms.common.InvalidClientIDException: Client id, ChinaClient, is in use. The reason for rejection is "The JNDI name weblogic
.jms.connection.clientid.ChinaClient was found, and was bound to an object of type weblogic.jms.frontend.FEClientIDSingularAggregatable : FEClientIDSingularAggr
egatable(SingularAggregatable(<6539122430256738311 .1="">:4):ChinaClient)"
at weblogic.jms.dispatcher.DispatcherAdapter.convertToJMSExceptionAndThrow(DispatcherAdapter.java:110)
at weblogic.jms.dispatcher.DispatcherAdapter.dispatchSync(DispatcherAdapter.java:45)
at weblogic.jms.client.JMSConnection.setClientIDInternal(JMSConnection.java:660)
at weblogic.jms.client.JMSConnection.setClientID(JMSConnection.java:630)
at weblogic.jms.client.WLConnectionImpl.setClientID(WLConnectionImpl.java:662)
at DurableTopicReceiveClient.init(DurableTopicReceiveClient.java:72)
at DurableTopicReceiveClient.main(DurableTopicReceiveClient.java:104)
Caused by: weblogic.jms.common.InvalidClientIDException: Client id, ChinaClient, is in use. The reason for rejection is "The JNDI name weblogic.jms.connection.
clientid.ChinaClient was found, and was bound to an object of type weblogic.jms.frontend.FEClientIDSingularAggregatable : FEClientIDSingularAggregatable(Singula
rAggregatable(<6539122430256738311 .1="">:4):ChinaClient)"
at weblogic.jms.frontend.FEConnection.setConnectionClientId(FEConnection.java:1170)
at weblogic.jms.frontend.FEConnection.invoke(FEConnection.java:1649)
at weblogic.messaging.dispatcher.Request.wrappedFiniteStateMachine(Request.java:961)
at weblogic.messaging.dispatcher.DispatcherServerRef.invoke(DispatcherServerRef.java:276)
at weblogic.messaging.dispatcher.DispatcherServerRef.handleRequest(DispatcherServerRef.java:141)
at weblogic.messaging.dispatcher.DispatcherServerRef.access$000(DispatcherServerRef.java:34)
at weblogic.messaging.dispatcher.DispatcherServerRef$2.run(DispatcherServerRef.java:111)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

这说明,Client ID只能被一个持久化Topic接收方使用,具有排他性。也就是说,多个持久化Topic接收方,需要使用多个Client ID。
(4)复制DurableTopicReceiveClient.java,生成DurableTopicReceiveClient2.java
只把China的地方改成Beijing。
public void init(Context ctx, String topicName) throws NamingException, JMSException {
tconFactory = (TopicConnectionFactory)ctx.lookup(JMS_FACTORY);
tcon = tconFactory.createTopicConnection();
tcon.setClientID("BeijingClient");
tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Topic)ctx.lookup(topicName);
tsubscriber = tsession.createDurableSubscriber(topic,"BeijingSubscription");
tsubscriber.setMessageListener(this);
tcon.start();
}
(5)在1个DOS命令窗口中启动消息消费者:java DurableTopicReceiveClient2 t3://localhost:7001
我们意外地发现,在Console中的Topic Durable Subscribers中“自动”增加了Beijing持久化订阅者。
也就是说,不用在Console事先配置,这个特性比较好,省得为每一个持久化订阅者人工配置。


(6)在消息生产者DOS窗口连续发送2条消息:msg1、msg2。
(7)观察两个消息消费者DOS窗口,发现两个窗口都“消费”了所有消息。
(8)停掉两个持久化消费者。
(9)在消息生产者DOS窗口连续发送2条消息:msg3、msg4。
(10)观察两个消息消费者DOS窗口,发现两个窗口都“消费”了所有消息。

代码下载:DurableTopicSubscriber.7z

2011年12月13日星期二

WLS_077:安装与使用WebLogic Server 12c 开发版

2011年12月12日,WebLogic Server 12c终于千呼万唤始出来。
本文介绍如何安装与使用WebLogic Server 12c 开发版。

1. 下载wls1211_dev.zip

2. 解压缩到指定目录,如C:\Oracle\wls1211_dev

3. 在C:\Oracle\wls1211_dev目录下创建文件:setEnv.cmd
setEnv.cmd内容如下:
set JAVA_HOME=C:\Oracle\Java\jdk1.6.0_25
set MW_HOME=C:\Oracle\wls1211_dev
set JAVA_VENDOR=Sun

4. 在C:\Oracle\wls1211_dev目录下执行脚本文件
(1)执行刚刚创建的setEnv.cmd。
(2)执行configure.cmd。
(3)执行%MW_HOME%\wlserver\server\bin\setWLSEnv.cmd。

5. 创建Domain
在C:\Oracle\wls1211_dev目录下执行脚本文件:%MW_HOME%\wlserver\common\bin\config.cmd。

6. 启动AdminServer,访问Console
惊喜的发现AdminServer启动比以前快了很多,只需大约10秒钟。
以后做实验就用开发版的WebLogic Server 12c吧。

2011年12月11日星期日

WLS_076:WebLogic Server高级管理之六:集群下的JMS配置

运行环境:WebLogic Server 10.3.5 + Oracle Database 10g Express Edition 10.2.0.1。
本实验要求首先完成《配置JMS》、《集群配置》和《为集群配置Apache代理》。

在WebLogic Server中,JMS Server是依附于Managed Server而运行的,不能独立运行,因此也就不存在把JMS Server 部署到集群上的这种设置。
每一个JMS Server上有各种JMS 资源如Queue和Topic;且每一种JMS资源都是pinned to JMS Server的。所以,普通的JMS资源无法实现集群下的负载均衡和容错功能。
JMS集群是通过配置Distributed JMS资源来实现的,比如distributed Queue。
一个Distributed Queue是一个虚拟的JMS资源,即并没有一个物理的JMS Queue对应它。
它是通过在每一个JMS Server上创建一个物理的JMS Queue来实现负载均衡的。
注意,这些JMS Server对应的Managed Server是属于一个集群的。
用户并不知道消息是发送到了哪些物理Queue中(他不需要知道),他只知道是发送到了Distributed Queue中。
设计图如下:


具体设置步骤如下:

1. 把message应用部署到集群上。

2. 创建JMS Server: dizzyworldJMSServer2,部署到dizzy2上。

3. 创建JMS Module: dizzyworldClusterJMSModule,部署到集群上。

4. 在dizzyworldClusterJMSModule中创建Subdeployments:dizzyworldClusterSubdeployment,部署到集群上。

5. 在dizzyworldClusterJMSModule中创建Distributed Queue:dizzyworldDistributedQueue,部署到dizzyworldClusterSubdeployment上。
点击dizzyworldDistributedQueue,查看其Member成员,会发现在JMS Server和Server2中各自创建了一个Queue。
这些Queue才是真正的物理Queue。


6. 在dizzyworldClusterJMSModule中创建Connection Factory:dizzyworldConnectionFactory,部署到dizzyworldClusterSubdeployment上。
点击dizzyworldConnectionFactory,选择Configuration-->Load Balance中,在不要选中“Server Affinity Enabled”。
这样做是为了“去亲属化”,否则每个JMS Server会优先考虑把消息放到自己的Queue中。


7. 运行http://localhost/message。
向dizzyworldDistributedQueue中发送三条不一样的信息。

8. 在Console中,点击dizzyworldDistributedQueue,选择Monitor:
会看到两个JMS Server上的Queue中收到了消息。

注意,处理请求的Managed Server,与消息是否发送到该Server所对应的JMS Server没有必然关系。
比如,我的实验结果是:dizzy2处理了2个请求,dizzy1处理了1个请求,但是JMS Server2中的Queue只有1条消息,而JMS Server中的Queue有2条消息。
这应该是“Server Affinity Enabled”选项的作用。

WLS_075:WebLogic Server高级管理之五:集群下的Data Source配置

运行环境:WebLogic Server 10.3.5 + Oracle Database 10g Express Edition 10.2.0.1。

本实验要求首先完成《配置Data Source (2)》、《集群配置》和《为集群配置Apache代理》。

1. 把testds应用target到dizzyworldCluster上

2. 把dizzyworldDS target到dizzyworldCluster上

完成这两个步骤后,实现了如下设计:

但是这个设计有个问题:一旦dizzyworldDS出现了故障,或者HRDatabase出现了问题,应用和Data Source即使发布到了集群上,也无法继续访问数据库了。

为了达到真正的Data Souce集群,设计上需要作如下改动:
(1)数据层应该有两个物理的数据库,这两个库之间的数据是同步的(使用数据库同步工具)。
(2)创建两个Data Source,分别指向这两个物理的数据库。
(3)创建一个Multi Data Source,包含这两个Data Source。
这样,应用中只需要使用一个Multi Data Source,无需关心使用的到底是哪个Data Source,哪个数据库。
最终的设计如下:


我们按以下步骤验证我们的设计:

3. 访问http://localhost/testds
点击Test Data Source按钮,观察是哪个Server响应的这个请求。
发现dizzy2输出了如下信息,说明dizzy2响应了请求:
Looking up the dizzyworldDS data source.
Getting the connection from the database.
Querying the database.

4. 把dizzyworldDS暂停,模拟dizzyworldDS出现故障


5. 重新访问http://localhost/testds
点击Test Data Source按钮,出现如下异常:
Error 500--Internal Server Error
java.lang.AssertionError: Unexpected Exception:
at weblogic.jdbc.common.internal.RmiDataSource.getConnection(RmiDataSource.java:273)
at jsp_servlet.__testdatasource._jspService(__testdatasource.java:122)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:34)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:183)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3717)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)

原因分析:从原理上讲,部署在集群上的应用应该可以访问到部署到集群上的Data Source,但是由于本实验中的应用部署在本地的Server上,也就是说Data Source和应用都部署在本地的Server上。
在这种情况下,为了提高性能,本地的应用pinned to Data Source,因此Data Source出现故障后,本地应用也就无法访问了。
如果应用不是部署在本地,那么应该可以访问到部署到集群上的Data Source,因为远程的应用没有pinned to Data Source。(这个说法来自官方说明,我没有验证)

彻底解决这个问题的办法是使用Multi Data Source。
6. 创建Multi Data Source:dizzyworldMultiDS

具体设置如下:
Name: dizzyworldMultiDS
JNDI Name: dizzyworldMultiDS
Algorithm Type: Failover
Target: dizzyworldCluster
Driver: Non-XA Driver
Data Sources: dizzyworldDS, dizzyworldDS2

7. 重新访问http://localhost/testds
Data Source Name:dizzyworldMultiDS
点击Test Data Source按钮,我们发现,即使dizzyworldDS处于暂停状态,应用仍然可以访问:

2011年12月10日星期六

WLS_074:WebLogic Server高级管理之四:为集群配置Apache代理

运行环境:WebLogic Server 10.3.5 + Oracle Database 10g Express Edition 10.2.0.1。

1. 下载并安装Apache
目前Apache最新的版本是2.2.21。


2. 为Apache安装WebLogic DSO:mod_wl_22.so
在[middleware_home]\[wls_home]\server\plugin目录下找到对应操作系统的plugin。
我这里使用的是plugin\win\32目录下的mod_wl_22.so。
把mod_wl_22.so复制到[apache_home]\modules目录下。

3. 修改httpd.conf配置文件
找到#LoadModule vhost_alias_module modules/mod_vhost_alias.so这一行,
在下面增加如下脚本:
LoadModule weblogic_module modules/mod_wl_22.so

<IfModule mod_weblogic.c>
WebLogicCluster 127.0.0.1:7003,127.0.0.1:7005,127.0.0.1:7007
MatchExpression /*
</IfModule>
<Location /weblogic>
SetHandler weblogic-handler
WebLogicCluster 127.0.0.1:7003,127.0.0.1:7005,127.0.0.1:7007
DebugConfigInfo ON
PathTrim /weblogic
</Location>

重新启动Apache。

4. 启动WebLogic Server:Admin、dizzy1、dizzy2
访问http://localhost/shoppingcart/,出现画面,说明Apache代理配置成功。
你可以按照上一个实验重新测试集群下的HTTP Session复制功能,只不过这次使用的是Apache代理。

Project下载:Apache-2.2.21.7z

参考文献:
1. http://docs.oracle.com/cd/E11035_01/wls100/plugins/apache.html