2011年6月13日星期一

OSB_014:OSB 11g 开发指南之四:使用OWSM配置安全策略

说明:本实验是OSB 11g 开发指南之三的继续。
为了提高信用卡验证服务的高可用性,我们增加了多个服务提供者,它们对安全认证的要求各不相同。
为了保持服务入口的安全策略不变,我们统一规定:OSB上的ValidateCredit Proxy Service需要按照UserNameToken的方式验证。

应用设计如下:


重要步骤说明:
1. 准备工作:配置Keystore。
(1)把default-keystore.jks复制到C:\Oracle\Middleware\user_projects\domains\soa_osb_domain\config\fmwconfig目录下。
(2)在EM中在EM中配置Keystore。




(3)在OSB Console中增加用户:joe。
(4)在EM中Credentials中增加Key:joe-key。


2. 保护ValidateCredit Proxy Service,为其配置OWSM策略:oracle/wss_username_token_service_policy。





测试时,不能像以前那样输入数据直接执行,必须要配置oracle/wss_username_token_client_policy,并且附上joe-key。


3. 在JDeveloper中,为getCreditCardStatus Reference配置oracle/wss_username_token_client_policy和joe-key。
右键点击getCreditCardStatus Reference,选择“Config WS Policies..."。




重新发布POProcessing,可以像往常一样测试,因为getCreditCardStatus Reference已经配置了oracle/wss_username_token_client_policy和joe-key。
在实际情况中,应该按登录用户的key来验证,如何配置呢?

3. 从OSB传播用户身份到SOA。
上面的实验是从SOA传播用户身份到OSB,现在我们要从OSB传播用户身份到SOA。
(1)为validationForCC中的getStatusByCC Service配置oracle/wss11_saml_token_service_policy。
可以在EM中配置,也可以在JDeveloper中配置。

(2)在OSB Console中为validationForCC Business Service配置oracle/wss11_saml_token_client_policy。
因为validationForCC Business Service指向的就是validationForCC SOA应用的入口。

2011年6月11日星期六

Tips_004:笔记本连接投影仪时如何让笔记本和投影仪各自的分辨率不变?

以前笔记本连接投影仪时,总是要调整笔记本的分辨率和投影仪的一样,才能在投影仪上显示全屏。
但是,笔记本的视野画面就变小了,操作起来很不方便。有没有一种方法让笔记本连接投影仪时,笔记本和投影仪各自的分辨率不变?
哈哈,经过试验,终于找到了方法:
桌面右健,选择分辨率,点击“连接到投影仪”,选择“扩展”,这时会出现两个显示器。
其中,第1个显示器是你的笔记本,第2个是投影仪,分别调整它们各自的分辨率,使得笔记本和投影仪的显示都达到最佳。

2011年6月9日星期四

Tools_012:如何使用DBank共享文件夹功能?

DBank是一个不错的网盘,你可以把一些文件上传后,供朋友们下载。
为了防止恶意用户删除你的文件,你可以使用DBank的“共享”功能。
这里创建了两个DBank用户,一个是自己用的,账户/口令不公开;另一个是公用的,账户/口令公开。

1. 以自己的账户登录DBank,选择要共享的文件夹,右键选择“共享”


2. 添加公用的账户


3. 允许公用的账户上传文件。


4. 设置共享文件夹的描述和链接URL,名称最好能够直观一些。


5. 以公用账户登录DBank,务必选择“切换到标准版”,否则看不到共享文件夹。
直接访问共享文件夹的URL也可以,比如:www.dbank.com/share/ADFRT20110610,然后用公用账户登录。


6. 公用账户可以查看、上传和下载文件,但是不可以删除。

2011年6月8日星期三

JVM_029:Win7下如何设置默认的JRE?

我的Win7上安装了两个JDK:Hotspot和JRockit。
在不设置任何环境变量的情况下(如JAVA_HOME和PATH),随便打开一个DOS命令窗口,运行java -version。显示如下信息:
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Oracle JRockit(R) (build R28.1.3-11-141760-1.6.0_24-20110301-1430-windows-x86_64, compiled mode)

很奇怪啊,它应该报告java命令找不到才对啊。
因为我是先安装的Hotspot,后安装的JRockit,所以JRockit的JRE变成了默认的JRE。
但是,系统在哪个地方记录了默认的JRE呢?
找来找去,发现系统使用的java实际上位于C:\Windows\System32目录下的java.exe。
是不是每安装一个JDK,该文件就会被覆盖?
这个等待我下次卸载全部JDK,并重新安装新版本后再来验证。

所以为了明确当前使用的是哪个JDK,使用前最好用java -version 确认一下。
如果不是自己希望的JDK,那就老老实实地设置如下环境变量:
set JAVA_HOME=C:\Oracle\Java\jdk1.6.0_25
set PATH=%JAVA_HOME%\bin;%PATH%

参考文献:
1. http://www.coderanch.com/t/411002/java/java/set-only-one-version-jre

2011年6月4日星期六

OSB_013:OSB 11g 开发指南之三:使用Direct Binding 完美集成SOA和OSB

到目前为止Oracle SOA上的应用和OSB上的应用运转一切正常。但是追求完美的心是每个优秀开发者所必须具备的素质。
他们自己提出了更高的要求:服务除了可以正确的调用之外,还应该能够传播安全和事务的上下文。
这是一项艰巨的工作,需要你对WebService 安全和事务的协议有深刻了解,不过那是在Direct Binding出现以前,呵呵,说话大喘气啊。
在Oracle 推出SOA Suite 11g PS2以后,有了Direct Binding,这个问题变得不能再简单了。
Direct Binding允许通过RMI调用SOA 组件,而我们知道RMI是支持安全和事务的上下文传播的。

1. 从OSB调用SOA
POProsssing 目前的访问方式是HTTP Binding,为了避免大的逻辑改动,我们先做一个小的实验验证:
金额<$1000的订单将通过Direct Binding调用POProcessing,金额>$1000的订单仍然按原来方式调用。

设计改造如下:


重要步骤说明:
1.1 在JDevloper中,修改POProcessing,增加receivePODirect Service。
拖放Direct Binding到Service栏。


1.2 在OSB上,根据receivePODirect WSDL创建Resource、Business/Proxy Service
增加receivePODirect Business Service,注意Transport Protocol为soa-direct:


修改原来的Proxy Service的路由规则,使用XQuery实现,脚本如下:
let $total := $body/ord:PurchaseOrder/ord:price * $body/ord:PurchaseOrder/ord:quantity
return
<ctx:route>
{
<ctx:service isProxy="false">
{
if ($total < 1000)
then "POProcessing/BusinessServices/receivePODirect"
else "POProcessing/BusinessServices/receivePO"
}
</ctx:service>,
<ctx:operation>execute</ctx:operation>
}
</ctx:route>

1.3 测试两个用例。
(1)小订单,如果在EM中看到实例的POProcessing的入口是receivePODirect,即宣告Direct Binding成功!
(2)大订单,仍然走的是receiveDirect,说明路由规则修改成功!

2. 从SOA调用OSB
信用卡验证服务目前的访问方式是HTTP Binding,该服务配置在OSB上,为了避免大的逻辑改动,我们先做一个小的实验验证:
卡号为1234-1234-1234-1234使用Direct Binding调用信用卡验证服务,其他卡号仍然按原来方式调用。

设计改造如下:


重要步骤说明:
2.1 在OSB中,修改Credit Service。
根据已有的Business Service,创建ValidateCredit_SB Proxy Service,注意Transport Protocol为sb:


2.2 在JDeveloper中,增加Direct Binding reference,指向ValidateCredit_SB Proxy Service。
拖放Direct Binding到Reference栏,其WSDL指向ValidateCredit_SB。


连接ApproveLargeOrder和DirectGetCCStatus:


修改ApproveLargeOrder中,调用Credit Card Service逻辑:


2.3 测试两个用例。
(1)大订单,订单号为1234-1234-1234-1234,如果在EM中看到实例的POProcessing调用了DirectGetCCStatus,即宣告Direct Binding成功!
(2)大订单,订单号为其他值,仍然走的是getCreditCardStatus,说明ApproveLargeOrder 修改成功!

2011年6月2日星期四

OSB_012:OSB 11g 开发指南之二:利用JCA Adapter “虚拟化”服务

POProcessing只是订单的入口,谁可以下订单呢?
这里我们假设某个ERP系统负责下订单,该系统负责搜集“需求”,比如根据“进销存”的情况,自动生成生成订单,即“按需下单”。
应用设计如图:Procure-to-Pay是一个“订单生成系统”,Order-to-Cash是“订单处理系统”。



“订单生成系统”最终会把需求数据插入到一张表中:OSB_Requisition。因为这里无法真实模拟ERP系统,所以我们在OSB上建立一个Requisition服务,由它代替ERP系统,完成向OSB_Requisition表插入数据的工作。
由于这里涉及数据库的写操作,因此我们要使用SOA中的Database Adapter。在真实场景中,要换成使用ERP Adapter。不管使用哪种Adapter,在OSB上对应使用的Transport Protocol都是JCA。
基本设计如下:


重要步骤说明:

1. 在JDeveloper中创建SOA项目,拖放Database Adapter到Reference中。
写操作属于Outbound,所以要放在“右边”Reference中。
设置JNDI Name,该值必须在Console中的DbAdapter中可以找到。选择Insert操作。选择主键。



2. 在OSB中创建Requisition项目,选择Create Resource from URL。
URL就是来自SOA项目中的file:/MyProject/SOA/OSBDemo/DBArtifacts/createRequisition_db.jca。
Resource Type选择JCA Binding。



3. 因为是Outbound服务,可以根据JCA 文件自动创建Business Service。



4. 根据已有的Business Service创建Proxy Service。
注意Transport Protocol要选择HTTP,因为Proxy Service是对外公开的。
Message Flow的设计:记录REQ_ID的信息,然后直接路由到Business Service。

5. 测试Proxy Service,可以测试三种情况,如果成功,应该在表OSB_Requisition中看到记录。

(1)<$1000的小订单
<cre:OsbRequisitionCollection xmlns:cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/createRequisition">
<cre:OsbRequisition>
<cre:requisitioner>Bob</cre:requisitioner>
<cre:reqid>2222</cre:reqid>
<cre:productname>iPod Shuffle</cre:productname>
<cre:item>1GB</cre:item>
<cre:itemtype>Electronics</cre:itemtype>
<cre:reqDate>March 16, 2010</cre:reqDate>
<cre:description>string</cre:description>
<cre:quantity>1.00</cre:quantity>
<cre:price>500.00</cre:price>
<cre:currency>USD</cre:currency>
<cre:deliverydate>April 16, 2010</cre:deliverydate>
<cre:plant>Boulder</cre:plant>
<cre:cctype>Mastercard</cre:cctype>
<cre:ccnumber>1234-1234-1234-1234</cre:ccnumber>
</cre:OsbRequisition>
</cre:OsbRequisitionCollection>

(2)>$1000 and <$5000的大订单

<cre:OsbRequisitionCollection xmlns:cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/createRequisition">
<cre:OsbRequisition>
<cre:requisitioner>Bob</cre:requisitioner>
<cre:reqid>3333</cre:reqid>
<cre:productname>iPod Shuffle</cre:productname>
<cre:item>1GB</cre:item>
<cre:itemtype>Electronics</cre:itemtype>
<cre:reqDate>March 16, 2010</cre:reqDate>
<cre:description>string</cre:description>
<cre:quantity>5.00</cre:quantity>
<cre:price>500.00</cre:price>
<cre:currency>USD</cre:currency>
<cre:deliverydate>April 16, 2010</cre:deliverydate>
<cre:plant>Boulder</cre:plant>
<cre:cctype>Mastercard</cre:cctype>
<cre:ccnumber>1234-1234-1234-1234</cre:ccnumber>
</cre:OsbRequisition>
</cre:OsbRequisitionCollection>

(3)>$5000的超大订单

<cre:OsbRequisitionCollection xmlns:cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/createRequisition">
<cre:OsbRequisition>
<cre:requisitioner>Bob</cre:requisitioner>
<cre:reqid>4444</cre:reqid>
<cre:productname>iPod Shuffle</cre:productname>
<cre:item>1GB</cre:item>
<cre:itemtype>Electronics</cre:itemtype>
<cre:reqDate>March 16, 2010</cre:reqDate>
<cre:description>string</cre:description>
<cre:quantity>20.00</cre:quantity>
<cre:price>500.00</cre:price>
<cre:currency>USD</cre:currency>
<cre:deliverydate>April 16, 2010</cre:deliverydate>
<cre:plant>Boulder</cre:plant>
<cre:cctype>Mastercard</cre:cctype>
<cre:ccnumber>1234-1234-1234-1234</cre:ccnumber>
</cre:OsbRequisition>
</cre:OsbRequisitionCollection>

数据进入向OSB_Requisition表后,我们使用一个触发器向OSB_PurchaseOrder表中插入数据,但是还需要“有人”来读取OSB_PurchaseOrder表的数据,从而触发POProcessing。
由于这里涉及数据库的读操作,我们同样要使用SOA中的Database Adapter。
基本设计如下:


重要步骤说明:

1. 在JDeveloper中打开SOA项目,拖放Database Adapter到Service中。
写操作属于Inbound,所以要放在“左边”Service中。
设置JNDI Name,该值必须在Console中的DbAdapter中可以找到。选择Poll操作。选择读取记录后删除。选择主键。

2. 在OSB中创建Requisition项目,选择Create Resource from URL。
URL就是来自SOA项目中的file:/MyProject/SOA/OSBDemo/DBArtifacts/receivePO_db.jca。
Resource Type选择JCA Binding。

3. 因为是Inbound服务,可以根据JCA 文件自动创建Proxy Service。
URL就是来自SOA项目中的file:/MyProject/SOA/OSBDemo/DBArtifacts/receivePO_db.jca。
Resource Type选择JCA Binding。

4. 根据POProcessing中的入口ReceivePO的WSDL创建Business Service。
URL就是来自SOA项目中的file:/MyProject/SOA/OSBDemo/DBArtifacts/receivePO_db.jca。
Resource Type选择JCA Binding。

5. 修改Proxy Service中的Message Flow,增加记录和转换功能。
其中最关键的是如何把从数据库表OSB_PurchaseOrder中读出的数据格式,转化成POProcessing接受的数据格式。
XQuery可以作为转换器,脚本如下:
declare namespace ns1 = "http://xmlns.oracle.com/pcbpel/adapter/db/top/receivePO";
declare namespace ns0 = "http://xmlns.oracle.com/ns/order";
declare namespace xf = "http://tempuri.org/PO%20Processing/Resources/dbPO_to_soaPO/";

declare function xf:dbPO_to_soaPO($osbPurchaseorder1 as element())
as element(ns0:PurchaseOrder) {
let $OsbPurchaseorder := $osbPurchaseorder1
return
<ns0:PurchaseOrder>
<ns0:CustID>{ data($OsbPurchaseorder/ns1:custid) }</ns0:CustID>
<ns0:ID>{ data($OsbPurchaseorder/ns1:id) }</ns0:ID>
{
for $productname in $OsbPurchaseorder/ns1:productname
return
<ns0:productName>{ data($productname) }</ns0:productName>
}
{
for $itemtype in $OsbPurchaseorder/ns1:itemtype
return
<ns0:itemType>{ data($itemtype) }</ns0:itemType>
}
{
for $price in $OsbPurchaseorder/ns1:price
return
<ns0:price>{ data($price) }</ns0:price>
}
{
for $quantity in $OsbPurchaseorder/ns1:quantity
return
<ns0:quantity>{ data($quantity) }</ns0:quantity>
}
{
for $status in $OsbPurchaseorder/ns1:status
return
<ns0:status>{ data($status) }</ns0:status>
}
{
for $cctype in $OsbPurchaseorder/ns1:cctype
return
<ns0:ccType>{ data($cctype) }</ns0:ccType>
}
{
for $ccnumber in $OsbPurchaseorder/ns1:ccnumber
return
<ns0:ccNumber>{ data($ccnumber) }</ns0:ccNumber>
}
</ns0:PurchaseOrder>
};

declare variable $osbPurchaseorder1 as element() external;

xf:dbPO_to_soaPO($osbPurchaseorder1)

6. 测试Receive PO服务,分三种情况测试。
我们通过测试Requisition服务,从而间接地测试Receive PO服务。
因为测试Requisition服务时,会插入数据到OSB_Requisition表中,OSB_Requisition表上的触发器会把数据插入到表OSB_PurchaseOrder中,Receive PO服务会轮询OSB_PurchaseOrder表,一旦有数据后,会把数据转换并路由到POProcessing。
测试时,有一点感觉不太方便,因为我选择了读取OSB_PurchaseOrder表后就删除数据,
触发器的速度和轮询的速度一般要快于我查看的速度,所以无法判断是否真的有数据插入了OSB_PurchaseOrder表。
当然我可以根据Report记录,和POProcessing是否有新实例产生来判断,但多少有些麻烦。
如果OSB上有“暂停”某个服务的功能就好了,这样我可以先暂停Receive PO服务,测试Requisition服务,发现OSB_PurchaseOrder表中有数据后,再启动Receive PO服务,看看数据是否被读取并删除了。

总结:
当我们需要访问ERP或数据库这些比较“成熟而公认”的系统时,Oracle SOA 提供了对应的Adapter供你使用。
然后,在OSB上,Transport Protocol可以以JCA的方式与这些系统集成,非常方便,无需编码。
在数据格式转换环节,我们可以使用XQuery来实现。
这样,就完全实现了“服务虚拟化”:服务的请求者无需知道服务的提供者的具体技术实现,it just know, there is a service can satisfy its request,that is it.

2011年6月1日星期三

OSB_011:OSB 11g 开发指南之一:如何提高服务的高可用性?

本实验可以看做是实验《SOA Suite 11g 开发指南之十六:与 OSB(Oracle Service Bus)集成 》的继续。

POProcessing上线以后,客户迅猛增加,一些客户开始抱怨访问有时被拒绝。
仔细查看日之后,发现是Validate Credit Card服务不稳定,因为Validate Credit Card服务由第三方提供,并且已经挂接到服务总线上,所以问题不是出现在POProcessing中,那么该如何解决这个问题呢?

1. 手段一:增加多个服务提供者。
考虑到很多家银行都提供信用卡验证服务,我们决定在服务总线上多接入一些信用卡验证服务,这样哪个可以用就用哪个,提高服务的可用性。
而客户依然像平时一样访问POProcessing,无需知道调用的是哪个Validate Credit Card服务,也不需要修改客户端代码。

设计只需做一个小小的改动,如下:



重要步骤说明:

1.1 只需要在OSB上修改Business Service:Transport Configuration



1.2 增加多个Endpoint URI*



1.3 如果一切正常,运行一段时间后,应该能看到多个ValidateForCC服务,平均分配了请求。



2. 手段二:缓存结果到Coherence。
验证信用卡服务需要访问数据库,相对比较耗时,于是一个念头很自然的冒了出来:如果能把那些经常下大订单的VIP用户的信用卡返回信息缓存起来就好了,这样就可以从内存中读取。
OSB提供了这种能力:缓存结果。应用设计如下:



重要步骤说明:
2.1 修改Business Service 配置:
Result Caching:Supported
Cache Token Expression: $body/po:PO_ID
Expiration Time: 1 min


2.2 在Proxy Service的Message Flow中,设计如下:


在Request Pipeline中什么也不做,直接路由到Business Service,拿回响应后,在Response Pipeline中使用Replace Actity替换Header中的内容:
<caching-metadata>
<cache-originated>{$outbound/ctx:transport/ctx:response/tp:cache-originated/text()}</cache-originated>
<cache-token>{$outbound/ctx:transport/ctx:response/tp:cache-token/text()}</cache-token>
</caching-metadata>

2.3 使用另外一个Proxy Service:Test Service 来模拟Business Service最终调用的服务。Message Flow设计如下:
这给我们一个启示:如果Business Service无法访问,我们可以使用一个Proxy Service来模拟该Business Service。



在Request Pipeline中使用Java Call Activity调用一个Java类的sleep()方法,休眠5秒钟。
Java代码如下:

package oracle.osb.timer;

public class TimerImpl {

/**
* Sleep for the specified time (in seconds)
* @param time in seconds to sleep
*/
public static void sleep()
{
try
{
int time = 5;
System.out.println("+++++++++++++++TimerImpl.sleep: START SLEEP FOR " + time + " SECONDS");
Thread.sleep(time * 1000);
System.out.println("+++++++++++++++TimerImpl.sleep: COMPLETED SLEEPING FOR " + time + " SECONDS");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

在Response Pipeline中把如下SOAP消息返回:

<soap-env:Body>
<ns1:PurchaseOrder xmlns:ns1="http://xmlns.oracle.com/schemas/PO/">
<ns1:CustID>1111</ns1:CustID>
<ns1:ID>{$body/po:PO_ID/text()}</ns1:ID>
<ns1:productName>Bluetooth Headset</ns1:productName>
<ns1:itemType>Electronics</ns1:itemType>
<ns1:price>49.99</ns1:price>
<ns1:quantity>1</ns1:quantity>
<ns1:status>APPROVED</ns1:status>
<ns1:ccType>Mastercard</ns1:ccType>
<ns1:ccNumber>8765-8765-8765-8765</ns1:ccNumber>
</ns1:PurchaseOrder>
</soap-env:Body>

2.4 测试
使用同一个PO_ID,先后测试两次(两次间隔不超过1分钟),我们会发现第一次5秒钟才返回结果,第2次马上就返回了。这说明Result Cache起作用了。
如果超过一分钟后,再测试,会发现又是5秒钟后才返回结果。
你可以在返回的SOAP Header中看到是否使用了Cache,以及Token值。