﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-kapok-随笔分类-Weblogic Portal</title><link>http://www2.blogjava.net/kapok/category/829.html</link><description>垃圾桶,嘿嘿，我藏的这么深你们还能找到啊，真牛！</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:27:07 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:27:07 GMT</pubDate><ttl>60</ttl><item><title>SSL※ X509</title><link>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 29 Sep 2005 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/14377.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/29/14377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/14377.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/14377.html</trackback:ping><description><![CDATA[作者：Stealth <BR>翻译：nixe0n <BR><BR><A href="http://www.linuxbyte.net/view.php?skin=art&amp;ID=3320">http://www.linuxbyte.net/view.php?skin=art&amp;ID=3320</A><BR><BR><BR>简介 <BR><BR><BR>SSLv3的当前版本是3.1版，也被称为TLS。它提供了一种机制，在网络上进行安全的数据传输。它据说能够满足所有的安全需要，比如：你的银行帐户管理。 <BR><BR>但是在这里我将告诉你，这实际上是不可能的。 <BR><BR>在本文中，我首先将对SSL做一些介绍，这是非常重要的。不过，这里我们不会介绍诸如SSL是怎样实现连接的等深层问题，如果有兴趣，可以自己参考参考资料。 <BR><BR><BR><BR>1.为什么使用SSL <BR><BR><BR>SSL是为了实现网络数据传输中的如下目的设计的： <BR><BR><BR><BR>机密性 <BR><BR>这是通过对数据进行加密实现的，在进行SSL握手时，SSL选择一种对称算法对数据进行加密，然后才在网络上传输数据。SSL使用的加密算法有好多种，如果某种算法被新的网络攻击方法识破，它只要选择另外的算法就可以了。 <BR><BR><BR>消息的完整性 <BR><BR>SSL使用一种很健壮的信息验证码(Message Authentication Code)，例如：SHA-1，验证码被放在数据包的后部，并且和数据一块被加密。这样，如果数据被修改，其散列值就无法和原来的验证码匹配，从而能够检测出数据是否被修改。MAC同时也被用于保护SSL连接免受干扰。 <BR><BR><BR><BR>保护数据免受重放攻击(replay-attack) <BR><BR>SSL使用序列号来保护通讯方免受报文重放攻击。这个序列号被加密后作为数据包的负载。在整个SSL握手中中，都有一个唯一的随机数来标记这个SSL握手，这样重放便无机可乘。 <BR><BR><BR>免受recorder攻击(reorder-attack) <BR><BR>上面所说的序列号也可以防止攻击者记录数据包并以不同的次序发送。 <BR><BR><BR><BR>端点验证 <BR><BR>使用X509(当前版本是3)证书，SSL支持客户和服务器的验证。关于服务器的连接，我们稍后将深入介绍。 <BR><BR><BR>这听起来似乎很安全，但是看了本文介绍的程序，就就不这么想了。(不过，我们还不能打破客户端的验证) <BR><BR>使用本文介绍的攻击方法，我们就可以看到SSL连接上所有明文数据，根据我们的需要修改传输的数据，对数据进行中继发送，以错误的次序发送甚至丢弃我们不需要的报文。这种攻击方法就是所谓的途中人攻击(man in the middle attack，或者中间人攻击)。 <BR><BR><BR><BR>2.X509数字证书 <BR><BR><BR>X509数字证书是SSL的一个组成部分。在SSL握手过程中，服务器向客户发送自己的数字证书。一个X509数字证书包括发行者的识别名(Distinguished Name)、主体(Subject)的识别名、一个版本号和序列号、选择的算法、密钥的有效期时间窗，还有主体的公钥。 <BR><BR>主体(subject)是这个证书包含实体的名，证书中的公钥属于主体(Subject)。在平常的X509数字证书中，没有标志DNS名的域。通常，CN域被影射为DNS名，但是这只是一个客户和数字证书的实体必须都认可的协议。 <BR><BR>发行者(issuer)是使用自己的私钥签发这个数字证书。它叫做数字证书中心(Certificate Authority,CA)。 <BR><BR>让我们看一个X509数字证书： <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 1 (0x0) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net/Email=crew@segfault.net <BR>Validity <BR>Not Before: Nov 19 01:57:27 2000 GMT <BR>Not After : Apr 5 01:57:27 2028 GMT <BR>Subject: C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:cd:64:2a:97:26:7a:9b:5c:52:5e:9c:9e:b3:a2: <BR>e5:f5:0f:99:08:57:1b:68:3c:dd:22:36:c9:01:05: <BR>e1:e5:a4:40:5e:91:35:8e:da:8f:69:a5:62:cf:cd: <BR>70:dc:ca:d2:d7:92:03:5c:39:2a:6d:02:68:91:b9: <BR>0d:d1:2c:c7:88:cb:ad:be:cc:e2:fa:03:55:a1:25: <BR>47:15:35:8c:d9:78:ef:9f:6a:f6:5f:e6:9a:02:12: <BR>a3:c2:b8:6a:32:0f:1d:9d:7b:2f:65:90:4e:ca:f7: <BR>a0:e4:ae:55:91:09:e4:6e:01:e3:d1:71:1e:60:b1: <BR>83:88:8f:c4:6a:8c:bb:26:fd <BR>Exponent: 65537 (0x10001) <BR>Signature Algorithm: md5WithRSAEncryption <BR>7d:c7:43:c3:71:02:c8:2f:8c:76:9c:f3:45:4c:cf:6d:21:5d: <BR>e3:8f:af:8f:e0:2e:3a:c8:53:36:6b:cf:f6:27:01:f0:ed:ee: <BR>42:78:20:3d:7f:e3:55:1f:8e:f2:a0:8e:1a:1b:e0:76:ad:3e: <BR>a0:fc:5b:ce:a6:c4:32:7b:64:f2:a4:0f:a3:be:a1:0e:a7:ca: <BR>ed:67:39:07:65:6b:cc:e7:5a:9a:b0:3a:f3:5c:1a:18:d4:dd: <BR>8c:8d:5a:9e:a0:63:e0:7d:af:7c:97:7c:89:17:0f:25:2f:a7: <BR>80:d3:02:dc:88:7a:12:64:ec:8a:ff:e4:62:92:2e:7f:75:03: <BR>82:f1 <BR><BR><BR>要点： <BR><BR><BR>Issuer: C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net/Email=crew@segfault.net <BR><BR><BR>C、ST、L、O和Email构成发行者的识别名(distinguished name,DN)。 <BR><BR><BR>Subject:C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR><BR><BR>证书可以由一个公开的CA签发，或者由自己签发(就是所谓的自签发证书)。在这个例子中的证书就是由自己签发的。 <BR><BR>这是没有被拦截的原始数字证书。后面我们将看一下如果有人拦截连接看起来会怎样。 <BR><BR>当你的浏览器向<A style="COLOR: #003793" href="https://segfault.net/" target=_blank>https://segfault.net</A>的连接时，这个证书会在SSL握手期间进行交换。证书中保存的公钥就被用于会话的加密。 <BR><BR>为了具有pretty good层的安全性，证书应该由一个CA(你自己或者一个公开的CA)签发，客户有这个CA的公钥用于检查这个证书的合法性。如果客户没有这个CA的公钥，浏览器就会提示用户接受还是拒绝这个证书。这对于交互式的客户程序是必须的，不过事实上对于太多的站点发行的证书，客户并没有他们的公钥来检查证书的合法性。对于普通的交互式客户程序(例如：Netscape浏览器)，这种情况就可能造成使SSL连接失去意义。 <BR><BR><BR><BR>3.拦截 <BR><BR><BR>综上所述，X509数字证书是SSL的重要环节。它的任务就是保证客户和服务器之间的会话，并且它使用的密钥是正确的。 <BR><BR>现在，想象一下我们伪装一个证书，并对一个SSL连接进行转发会怎么样。 <BR><BR>这值得一试。我们的座右铭是“teile und herrsche（哪国英文？）”，这里我们必须解决两个问题： <BR><BR><BR><BR>a.劫持连接然后进行转发。 <BR><BR><BR>b.伪造一个数字证书，让客户以为他是和真正的服务器通讯。 <BR><BR><BR>a+b就是通常所说的途中人(man in the middle，又可以叫做中间人)攻击。从理论上X509应该能够阻止这种攻击，但是平常的交互式客户程序(例如：Netscape浏览器)所采取的证书检查方式使这种攻击方式有机可乘。 <BR><BR>第一个问题很好解决，假设我们位于客户和服务器之间，我们只要在我们的防火墙上略施小计(最好是在Linux或者BSD上:P)把连接重定向就可以了，另外我们把自己的程序称为mimd。对于Linux-2.2.x(ipchains)版本的内核，使用如下规则就可以截获https包文，把它们导入输入(input)链: <BR><BR><BR>ipchains -A input -s 0/0 -d 0/0 443 -j REDIRECT 10000 -p tcp <BR><BR><BR>对于Linux-2.4.x内核(iptables)，可以使用如下规则： <BR><BR><BR>iptables -t nat -A OUTPUT -p tcp --sport 1000:3000 -dport 443\ <BR>-j REDIRECT --to-port 10000 <BR><BR><BR>要给出SSL客户的源端口，如果我们忽略了这一点，mimid就会进入一个无限的循环(iptables将会重定向已经重定向的流量)。mimd被绑定到8888端口，它不匹配这条规则。你的物理位置不必位于SSL连接双方中间，位于服务器局域网内或者客户机局域网内就足够了。使用ARP欺诈就可以很好地完成这个工作，甚至连防火墙规则都不必修改。 <BR><BR>有了这些重定向规则，我们就可以着手建立的工具了。目标地址可以使用操作系统的API找到(getsockopt())。这个工具中的NS_Socket::dstaddr()函数在绝大多数操作系统中都可以成功编译。使用这个小程序，我们可以看到通过连接的数据。 <BR><BR>为了使这个小程序能够看到连接的明文数据，我们需要使用SSL_accpet()和SSL_connect()调用。首先，我们需要调用(accept()接受客户程序的连接，接着使用SSL_connect()向真正的服务器发起连接请求。然后，执行真正的SSL_accept()。假设我们已经完成了初始化内容，比如：加载密钥文件等。这时，在客户端，客户程序(例如：浏览器)就会询问用户是否接受这个工具的证书。 <BR><BR>但是，用户显然可以轻松认出这个证书是伪造的，因为他在浏览A公司的网站时，却收到B公司或者途中人的证书，必然会引起他的怀疑。 <BR><BR>下面我们将会解决这个问题。SSL_connect()和SSL_accept()的顺序应该正确，我将对其进行解释。 <BR><BR><BR><BR>4.DCA <BR><BR><BR>如果用户接受伪造的证书，我们就可以使用SSL_read()读出连接的明文数据，并使用SSL_write()把它们转发到真正的服务器。现在我们就着手解决如何伪造证书的问题。 <BR><BR>记住：在SSL_connect()要先于SSL_accept()调用，这样服务器可以把我们看做合法的用户，和我们进行正常的SSL握手，从而我们可以得到服务器的证书。 <BR><BR>下面我们看一下实际的代码： <BR><BR><BR>//阻塞，等待iptables劫持的连接 <BR>while ((afd = accept(sfd, (sockaddr*)&amp;from, &amp;socksize)) &gt;= 0) { <BR><BR>// 获得连接真正的 <BR>// 目的地址 <BR>if (NS_Socket::dstaddr(afd, &amp;dst) &lt; 0) { <BR>log(NS_Socket::why()); <BR>die(NULL); <BR>} <BR><BR>... <BR><BR>++i;//一个全局变量记录被劫持连接的数量 <BR>if (fork() == 0) {//fork出一个进程，由子进程处理被劫持的连接，父进程继续等待连接 <BR><BR>// 成为真正目的服务器的客户 <BR>if ((sfd2 = socket(PF_INET, SOCK_STREAM, 0)) &lt; 0) { <BR>log("main::socket"); <BR>die(NULL); <BR>} <BR><BR><BR>if (NS_Socket::bind_local(sfd2, 8888+i, 0) &lt; 0) { <BR>log(NS_Socket::why()); <BR>die(NULL); <BR>}//把套接字绑定到本地端口，可以同时处理多个连接 <BR><BR><BR>// 向真正的服务器发起连接 <BR>if (connect(sfd2, (struct sockaddr*)&amp;dst, <BR>sizeof(dst)) &lt; 0) { <BR>log("main::connect"); <BR>die(NULL); <BR>} <BR><BR>... <BR><BR>client-&gt;start(); <BR>client-&gt;fileno(sfd2); // 使用sfd2和真正的服务器进行连接 <BR><BR>// 进行SSL握手以建立SSL连接 <BR>if (client-&gt;connect() &lt; 0) { <BR>log("Clientside handshake failed. Aborting."); <BR>die(NULL); <BR>} <BR><BR><BR>现在，我们和真正服务器之间的SSL握手已经完成。注意，在源代码中，SSL_connect()和SSL_accept()两个函数都被封装到了client和server对象中。现在，我们可以准备自己作为SSL服务器和SSL客户之间的连接了： <BR><BR><BR>// 服务器端 <BR><BR>server-&gt;start(); // 建立SSL对象 <BR>server-&gt;fileno(afd); // 使用afd套接字接受客户连接 <BR><BR><BR>我们进行真正的伪造，调用SSL_accept()： <BR><BR><BR>if (enable_dca) <BR>NS_DCA::do_dca(client, server); <BR><BR><BR>动态证书装配(Dynamic Certificate Assembly)函数do_dca()进行如下工作： <BR><BR><BR>给一个几乎是空白的证书(除了C域之外，其它RDN全部为空)，do_dca()使用和服务器进行SSL握手获得的内容填充剩下的RDN域。我们抽取L、ST、O、CN、OU和EMAIL域，把它们放到我们自己的证书，然后把这个证书显示给SSL客户。为了完成这个工作，do_dca()使用了字符串解析(抽取RDN域)，并调用OpenSSL提供的using X509_()函数。 <BR><BR>在证书发行者的OU域(OrganizationalUnit，原来为空)我们填上一个空格，这个空格不会在SSL客户程序的窗口中显示出来，但是可以把伪造的新证书和来自公共CA的证书区别开。当这个伪造的证书到达SSL客户程序之后，客户程序就会提示用户是否接收这个证书，用户看来这个证书来自一个已知的可信任的CA(实际上，OU域多了一个空格:P，但是用户看不出来)，而对于SSL客户程序来说，它知道这个证书不是来自用户看到的CA(差一个空格呢:P)，因此找不到这个CA的公钥，只好提示用户，让用户定夺是否接受这个证书。这时，被愚弄的用户一般会接受这个证书。 <BR><BR>现在我们可以修改发行者的subject域(CN...)，把前面的X509证书变成自签发(self-signed)证书。用户无法知道自签发证书是伪造的。 <BR><BR><BR>然后，把被重新装配的证书显示给客户： <BR><BR><BR>// do SSL handshake as fake-server <BR>if (server-&gt;accept() &lt; 0) { <BR>log("Serverside handshake failed. Aborting."); <BR>die(NULL); <BR>} <BR><BR>ssl_forward(client, server); <BR><BR><BR>ssl_forward()函数只是循环调用SSL_read/SSL_write函数，记录传输的明文数据。我们也可以随心所欲地修改传递的数据。 <BR><BR>下面在mimd激活之后(没有使用-I选项)，我们使用cf取回来自https服务器的X509证书： <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 3 (0x2) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=US, C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net, OU= /Email=crew@segfault.net <BR>Validity <BR>Not Before: Mar 20 13:42:12 2001 GMT <BR>Not After : Mar 20 13:42:12 2002 GMT <BR>Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: <BR>e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: <BR>94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: <BR>0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: <BR>b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: <BR>94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: <BR>ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: <BR>b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: <BR>ca:c2:07:9d:ba:32:ff:d6:e7 <BR>Exponent: 65537 (0x10001) <BR>X509v3 extensions: <BR>X509v3 Subject Key Identifier: <BR>4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>X509v3 Authority Key Identifier: <BR>keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>DirName:/C=US <BR>serial:00 <BR><BR>X509v3 Basic Constraints: <BR>CA:TRUE <BR>Signature Algorithm: md5WithRSAEncryption <BR>b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: <BR>ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: <BR>86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: <BR>d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: <BR>8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: <BR>f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: <BR>e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: <BR>eb:f2 <BR><BR><BR>比较激活mimd前后两个证书，你可以发现两个证书的公钥是不同的，后面这个证书的公钥实际是mimd自己的公钥。C域包含US和EU，这些信息会在Netscape浏览器中显示，和原来的证书没有什么不同。注意到这个证书的OU域了吗？原来的证书并没有这个域，而新的证书中这个域的是一个空格。新证书的发行者信息是来自原来的数字证书。这个证书不再是由某个公开CA签发的，变成了一个自签发(self-signed)的证书。 <BR><BR>下面我们使用-I选项重新启动mimd，这次我们使用被截获证书的Subject来填充伪造证书的Issuer，使伪造证书变成自签发证书。 <BR><BR><BR>stealth@lydia:sslmim&gt; ./cf segfault.net 443|openssl x509 -text <BR>Certificate: <BR>Data: <BR>Version: 3 (0x2) <BR>Serial Number: 1 (0x1) <BR>Signature Algorithm: md5WithRSAEncryption <BR>Issuer: C=US, C=EU, ST=segfault, L=segfault, <BR>O=www.segfault.net, OU= , CN=www.segfault.net/Email=crew@segfault.net <BR>Validity <BR>Not Before: Mar 20 13:42:12 2001 GMT <BR>Not After : Mar 20 13:42:12 2002 GMT <BR>Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, <BR>CN=www.segfault.net/Email=crew@segfault.net <BR>Subject Public Key Info: <BR>Public Key Algorithm: rsaEncryption <BR>RSA Public Key: (1024 bit) <BR>Modulus (1024 bit): <BR>00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: <BR>e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: <BR>94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: <BR>0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: <BR>b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: <BR>94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: <BR>ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: <BR>b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: <BR>ca:c2:07:9d:ba:32:ff:d6:e7 <BR>Exponent: 65537 (0x10001) <BR>X509v3 extensions: <BR>X509v3 Subject Key Identifier: <BR>4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>X509v3 Authority Key Identifier: <BR>keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD <BR>DirName:/C=US <BR>serial:00 <BR><BR>X509v3 Basic Constraints: <BR>CA:TRUE <BR>Signature Algorithm: md5WithRSAEncryption <BR>b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: <BR>ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: <BR>86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: <BR>d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: <BR>8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: <BR>f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: <BR>e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: <BR>eb:f2 <BR><BR><BR>比较这两个伪造的证书，你会发现后面这个的Issuer中包含CN域，这是为了加强伪造的效果:P。 <BR><BR><BR><BR>结论 <BR><BR><BR>综上所述，使用交互式客户程序在网络上冲浪的用户无法知道遭到了途中人攻击，因为他们无法分辨公司使用未知的CA(company uses unknown CA)的提示信息是真的还是自己遭到了途中人攻击。而且，即使他以前曾经浏览过这个站点并保存了它的数字证书，也仍然可能掉入圈套(还记得OU域的空格吗？:P)。对于一些机警的用户，把伪造的证书变成自签发证书将会打消他们的疑虑。 <BR><BR>本文使用separate-ports的方式断开了SSL连接，但是在一种情况下(upward negotiation)无法使用mimd。SSL使用一个关键词把前面的明文数据流变成SSL数据流传输。这个问题使用MSG_PEEK可能可以解决，他们(作者)正在研究呢:P <BR><BR><BR><BR>参考 <BR><BR><BR>[1] "SSL and TLS" Designing and Building Secure Systems <BR>Eric Rescorla, AW 2001 <BR><BR>A must-read if you want/need to know how SSL works. <BR><BR>[2] "Angewandte Kryptographie" <BR>Bruce Schneier, AW 1996 <BR><BR>THE book for crypto-geeks. I read the german version, <BR>in english its Applied Cryptographie <BR><BR>[2] various openssl c-files and manpages <BR><BR>[3] <A style="COLOR: #003793" href="http://www.cs.uni-potsdam.de/homepages/students/linuxer/sslmim.tar.gz" target=_blank>http://www.cs.uni-potsdam.de/homepages/students/linuxer/sslmim.tar.gz</A> <BR>A DCA implementation, described in this article; <BR>also contains cf tool. <BR><BR>[4] In case you cannot try mimd on your local box, view <BR>a snapshot from a mim-ed session provided by TESO: <BR><A style="COLOR: #003793" href="http://www.team-teso.net/ssl-security.png" target=_blank>http://www.team-teso.net/ssl-security.png</A> <BR><BR><BR>原文来源：Haaaang on snoopy, snoopy hang on. (SSL for fun and profit) phrack Volume 0x0b, Issue 0x39 <BR><img src ="http://www.blogjava.net/kapok/aggbug/14377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-29 09:40 <a href="http://www.blogjava.net/kapok/archive/2005/09/29/14377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Configuring and Managing WebLogic Server</title><link>http://www.blogjava.net/kapok/archive/2005/08/10/9735.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 10 Aug 2005 11:09:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/08/10/9735.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/9735.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/08/10/9735.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/9735.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/9735.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.tw/techdoc/01wp/01wp_041105_02.htm">http://dev2dev.bea.com.tw/techdoc/01wp/01wp_041105_02.htm</A><BR><BR><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/2004110503.html">http://dev2dev.bea.com.cn/techdoc/wlportal/2004110503.html</A><BR><BR>Configuring and Managing WebLogic Server<BR><A href="http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html">http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html</A><BR><BR>WebLogic Node Manager Service为产品环境中的Managed Server提供了管理功能，包括： 
<UL>
<LI>Managed Server的状态监控。 
<LI>Managed Server在进程或状态故障时自动重启。 
<LI>对Managed Server进行远程和集中控制，包括启动和停止服务器。 </LI></UL>
<P>　　Node Manager安全地提供这些功能，将双向的SSL身份验证、一个被允许控制Managed Server的可信主机列表、其他安全功能结合在一起。</P>
<P>　　Node Manager可以注册为Windows服务或UNIX守护程序，因此当机器启动或重启时，它能够自动启动计算机上的Managed Server。</P>
<P>　　本文讨论了如何为WebLogic Portal配置WebLogic Node Manager Service，并提供了一个示例配置顺序。</P>
<P>　　有关Node Manager的完整信息，请参阅<A href="http://e-docs.bea.com/wls/docs81/adminguide/nodemgt.html" target=_blank>http://e-docs.bea.com/wls/docs81/adminguide/nodemgt.html</A>。 <BR>Node Manager概览：<BR><BR>　　在高层次上，Node Manager使用被管理的服务器信息进行配置，并且使这些服务器保持在所希望的状态。</P>
<P>　　Node Manager只管理已经启动的托管服务器——Node Manager不影响手动启动的托管服务器。</P>
<P>　　每一台Managed Server运行的主机上能够运行一个Node Manager进程。Node Manager与该主机上的每个Managed Server保持一个socket连接，并使用该连接监视Managed Server的状态。如果Managed Server进程意料外故障或WebLogic确定该进程活着但处在失败或挂起状态，那么Node Manager能够自动重启Managed Server。 </P>
<P>　　除了自动重启功能，Node Manager还具有远程控制功能，因此管理员能够通过WebLogic Administration Console或WebLogic Admin工具启动或停止单个或所有Managed Server。在每个Managed Server主机上，Node Manager在配置的端口上监听服务器远程控制命令。</P>
<P align=center><IMG height=369 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110506.gif" width=482></P>
<P>　　Node Manager使用几个安全方法来限制对Node Manager Service、以及从Node Manager Service到Managed Server的远程管理访问。</P>
<UL>
<LI>所有从Node Manager Service到Managed Server的通信都是通过安全的双向SSL。每一方必须向另一方证明其身份。 
<LI>所有从管理客户端（WebLogic Administration Console或weblogic Admin工具）到Node Manager Service的通信也都是通过安全的双向SSL。每一方必须向其他一方证明其身份。 
<LI>Node Manager Service配置支持指定一个允许管理客户端连接的受信任主机列表。来自其他（非受信任）主机的连接是不被允许的。该功能能够用于拒绝来自除Administration Server主机之外的访问。 
<LI>也有其他一些安全选项是可用的，例如在一个具有多个网卡的系统上指定Node Manager Service应该监听的网络地址。 </LI></UL>
<P>　　Node Manager Service创建命令行，启动Managed Server进程，不需要调用启动脚本。因此，在启动一台Managed Server时指定标准startManagedServer脚本向操作系统发出的相同类型的信息是很重要的。 <BR><BR>　　结合使用Node Manager和WebLogic Portal：<BR>　　除了标准的Node Manager配置步骤，在配置Node Manager与WebLogic Portal协同工作时需要应用一个额外的步骤。</P>
<P>　　特别地，Administration Server域目录包含一个名为“wrspKeystore.jks”的文件。每个Managed Server需要访问服务器根目录上的一个目录中的该文件。</P>
<P>　　默认情况下，Node Manager将在WL_HOME/common/nodemanager的一个子目录下运行服务器，例如，WL_HOME/common/nodemanager/ms1/…。 </P>
<P>　　因此，如果Node Manager启动了目录WL_HOME/common/nodemanager/ms1/中一个名为ms1的Managed Server ，那么应该把wsrpKeystore.jks复制到每个Managed Server上的一个目录中，例如，WL_HOME/common/nodemanager/wsrpKeystore.jks。 <BR><BR>　　<B>Node Manager配置概述<BR></B>　　存在许多可能的配置变体，这些变体在<A href="http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html" target=_blank>http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html</A>中有详细描述。</P>
<P>　　一些主要的配置变体包括：</P>
<UL>
<LI>Node Manager作为一个系统服务/守护程序还是手动启动的进程运行。 
<LI>安全配置的类型和程度。 
<LI>Node Manager是否应该自动杀死崩溃或运行在故障状态的Managed Server进程。 </LI></UL>
<P>　　如果将Node Manager配置为服务器/守护程序，那么在系统启动时将会自动启动它。以这种方式配置，Node Manager能够在机器重启时自动重启Managed Server。<BR><BR>　　<B>Node Manager配置过程：</B><BR>　　在配置Node Manager中涉及的主要步骤包括：</P>
<UL>
<LI>指定安全配置和受信任主机。 
<LI>在每台Managed Server主机上，必须配置Node Manager进程，可选择注册为服务/守护程序。在该步骤中指定安全设置。 
<LI>在WebLogic Administration Console中，需要指定每台Managed Server运行在哪台主机上，以及Administration Server如何联系每台主机上的Node Manager服务。 
<LI>在WebLogic Administration Console中，Node Manager正在管理的每台Managed Server也必须进行配置，这样Node Manager就能够建立起要发送给操作系统的命令。我们将首先通过startManagedWeglogic脚本启动服务器、注意生成的命令行，并使用该信息正确配置每台Managed Server。 </LI></UL>
<P>　　<B>为WebLogic Portal配置Node Manager示例</B><BR>　　该示例配置用于具有一台Administration Server和两台Managed Server的域。每台服务器将运行在其自己的主机上。</P>
<P>Administration Server:<BR>- platform: Windows 2000<BR>- hostname: adminbox.foo.com<BR>- IP address: 192.168.1.51</P>
<P>Managed Server 1 (ms1):<BR>- platform: Windows 2000<BR>- hostname: host1.foo.com<BR>- IP address: 192.168.1.52</P>
<P>Managed Server 2 (ms2):<BR>- platform: Windows 2000<BR>- hostname: host2.foo.com<BR>- IP address: 192.168.1.53</P>
<P><B>　　选择安全配置和信任机制</B><BR>　　该示例产生环境配置使用完整安全模式。</P>
<P>　　我们将使用WebLogic演示SSL身份keystore (DemoIdentity.jks)和演示SSL发证机构（CA）信任keystore (DemoTrust.jks)，它们包含在WebLogic安装中，安装在WL_HOME/server/lib/*.jks。</P>
<P>　　身份keystore保存证明一方身份的SSL证书；信任keystore保存受信任发证机构的SSL证书，发证机构能够用于证实身份。运行时，服务器提供SSL证书，客户端根据列在其信任keystore中的CA进行检查。</P>
<P>　　SSL证书也包含一个主机身份，该身份嵌入在证书的“Subject”（主题）字段中。SSL客户端使用该主机身份和证书/信任发证机构匹配项确定远程服务器的证书是否可用。客户端验证两台服务器的身份证书都能够被客户端的信任发证机构所接受，客户端用于联系服务器的主机地址与列在证书中的主机身份匹配。</P>
<P>　　每台安装WebLogic的主机具有自己惟一的特定于主机的演示SSL证书，该证书与那台特定的主机的身份一起嵌入。该证书被嵌入到DemoIdentity.jks keystore中。主机身份能够通过Java keytool实用程序显示。我们后面将看一个例子。所有的演示SSL证书由同一个DemoTrust.jks信任发证机构签署，该机构对于所有主机都是一样的。</P>
<P>　　和双向SSL一起使用这些演示keystore，客户端将在DemoIdentity.jks中向一个服务器提供SSL证书，服务器将检查列在其DemoTrust.jks keystore中的CA SSL证书是否接受该证书。</P>
<P>　　这些演示keystores不提供安全的环境。每个WebLogic安装包括相同的演示 CA信任keystore文件（在WL_HOME/server/lib/DemoTrust.jks中）和生成DemoTrust.jks keystore中CA签署的特定于主机的演示身份证书的工具。因此，任何WebLogic安装都能产生被其他使用该DemoTrust.jks CA keystore的WebLogic安装所接受。</P>
<P>　　因此，对于一个真实的生产环境，您应该希望禁用演示CA信任keystore (DemoTrust.jks)，并使用一个有名的发证机构签署的SSL证书，或者至少是您自己定制的发证机构。有关更多信息，请参见<A href="http://e-docs.bea.com/wls/docs81/secmanage/ssl.html" target=_blank>http://e-docs.bea.com/wls/docs81/secmanage/ssl.html</A>。</P>
<P>　　因为SSL证书包含主机身份信息，所以当使用SSL证书时，特别是演示证书时，应当注意客户端用于联系服务器的主机名称必须和服务器证书中的主机身份匹配。</P>
<P>　　这暗示多数情况下主机名称是在一个域中配置的。它也暗示了DNS命名。</P>
<P>　　一个通用规则是：不要使用IP地址，而是使用主机名称。主机名称需要以正确格式指定。在指定一个主机名称时，当指定服务器地址时会为SSL证书主机名称格式使用接收服务器端的规则。例如，一个客户端使用的地址“fooHost”需要与服务器SSL证书主机身份字段匹配（如“fooHost”）。如果客户端正在使用“fooHost.foo.com”，服务器SSL证书的主机身份字段是“fooHost”，那么SSL连接将会失败。</P>
<P>　　例如，当一台Administration Server联系一个Managed Server时，为了使SSL配置工作，Administration Server用于联系Managed Server的主机地址必须与Managed Server主机的SSL证书主机身份匹配。</P>
<P>　　使用“keytool” Java实用程序，您能够检查嵌入在演示SSL证书中的主机名称身份，该证书嵌入在DemoIdentity.jks中。这里显示了如何做到这一点：</P>
<P>转到WL_HOME/server/bin。<BR>setWLSEnv.cmd/.sh<BR>cd ..\lib <BR>(WL_HOME/server/lib)<BR>keytool -list -v -alias demoidentity -keystore DemoIdentity.jks<BR>当提示输入keystore密码时，输入： <BR>DemoIdentityKeyStorePassPhrase<BR><BR>　　在输出（Owner字段）中、CN属性是嵌入到主机名称身份中的。</P>
<P>　　例如，在名为“host1.foo.com”的Windows主机中，您将看到像下面的输出：<BR><BR>D:\wl81sp3\weblogic81\server\lib&gt;keytool -v -alias demoidentity <BR>-list -keystore DemoIdentity.jks<BR>Enter keystore password: DemoIdentityKeyStorePassPhrase<BR>Alias name: demoidentity<BR>Creation date: Jun 24, 2004<BR>Entry type: keyEntry<BR>Certificate chain length: 1<BR>Certificate[1]:<BR>Owner: CN=host1, OU=FOR TESTING ONLY, O=MyOrganization, <BR>L=MyTown, ST=MyState, C=US<BR>Issuer: CN=CertGenCAB, OU=FOR TESTING ONLY, O=MyOrganization, <BR>L=MyTown, ST=MyState, C=US<BR>Serial number: -3cb71787b0af00ee4160180a08e071b0<BR>Valid from: Wed Jun 23 16:32:25 MDT 2004 until: Mon Jun 24 16:32:25 MDT 2019<BR>Certificate fingerprints:<BR>　　MD5: 34:B4:D4:86:28:06:9B:C2:29:A1:09:4B:A1:9C:77:55<BR>　　SHA1: F8:A0:AE:6C:A7:9B:CC:FD:0D:AE:17:8D:D9:C8:2F:E9:36:41:86:5B</P>
<P><BR>　　在这种情况下，SSL证书具有主机身份“host1”，因此无论什么时候客户端需要联系host1.foo.com，它需要指定“host1”。</P>
<P>　　对于Windows主机，嵌入在主机上DemoIdentity.jks中的演示SSL证书通常只是主机名称——例如，如果完整的主机名称是“host1.foo.com”，那么将把SSL证书链接到主机“host1”。在这种情况下，您将希望任何联系该Windows主机的客户端在连接时都使用DNS地址“host1”。</P>
<P>　　对于UNIX主机，嵌入在主机上DemoIdentity.jks中的演示SSL证书通常是完全限定的DNS名称——例如，如果完整的主机名称是“host5.foo.com”，那么将会把SSL证书链接到主机“host5.foo.com”。在这种情况下，您将希望任何联系该UNIX主机的客户端在连接时使用主机名称“host5.foo.com”，而不是“host5”或IP地址。</P>
<P>　　当一台Managed Server联系Administration Server时，完全相同的规则也适用。</P>
<P>　　就主机信任来说，我们将允许只有Administration Server主机能够联系Node Manager Service进程。这将允许我们从该Administration Serer主机使用WebLogic Administration Console或weblogic.Admin工具来控制Managed Servers，但拒绝来自任何其他主机的访问。 <BR><BR><B>　　在每台Managed Server主机上配置Node Manager进程：</B><BR>　　在每台运行着至少一个Managed Server的主机上执行这些步骤一次。在我们的示例配置域中，这些步骤将在主机host1.foo.com和host2.foo.com上执行。</P>
<P>　　1. 首先，让我们手动启动Node Manager，它将创建一些用于编辑的配置文件。默认情况下，Node Manager将在第一次启动时在目录WL_HOME/common/nodemanager下编写一些配置文件。 <BR>打开一个命令/终端窗口，cd WL_HOME/server/bin<BR>运行 “startNodeManager(.cmd/.sh)”来交互地启动Node Manager。 <BR>Node Manager将在WL_HOME/common/nodemanager中创建一些文件。<BR>在WebLogic Administration Console窗口中，等待直到看到一些下面形式的文本： </P>
<P>　　&lt;Jul 26, 2004 6:00:18 PM MDT&gt; &lt;Info&gt; &lt;NodeManager@*.*:5555&gt; &lt;NodeManager started, log messages being written into file ..., </P>
<P>　　然后输入ctrl-C关闭Node Manager。</P>
<P>　　2. 在WL_HOME/common/nodemanager/nodemanager.hosts中编辑受信任主机文件，以便指定被允许访问和控制将要运行在该主机上的Node Manager Service的远程主机集。每台受信任主机应该在单独一行输入。</P>
<P>　　您能够以主机名称或IP地址输入受信任主机。如果输入了主机名称，那么需要在nodemanager.properties文件中启用逆向DNS查找，我们将在下一步配置该文件。<BR>如果您希望Node Manager Service接受来自任何主机的远程命令，就在hosts文件中输入*。</P>
<P>　　在该示例中，我们希望限制只有Administration Server主机能够访问，因此我们输入Administration Server IP地址：192.168.1.51。</P>
<P>　　限制对受信任主机文件WL_HOME/common/nodemanager/nodemanager.hosts的访问。Node Manager Service在运行期间读取该文件，如果在该文件中添加了主机，它将动态启动接收来自那些主机的远程管理命令。因此，限制对该文件的访问很重要。</P>
<P>　　3. 编辑Node Manager 属性文件WL_HOME/common/nodemanager/nodemanager.properties，来指定SSL安全配置和其他选项。</P>
<P>　　这是指定SSL安全身份和信任keystore与访问信息（如别名和passphrase）的地方。</P>
<P>　　在运行时，Node Manager进程将为主机上的一台或多台Managed Server、可能也有Administration Server或weblogic.Admin工具提供一个SSL证书。所有这几方都将需要在信任keystore中拥有一个发证机构，该keystore接收提供的Node Manager SSL证书。另外，所有这几方都将在运行时向Node Manager进程提供它们自己的SSL证书。因此，Node Manager进程将需要在信任keystore中具有一个发证机构，该keystore接受这几方提供的SSL证书。</P>
<P>　　为了本示例的目的，我们将配置Node Manager进程使用DemoIdentity.jks keystore和DemoTrust.jks CA keystore。因为Administration Server、Managed Server和Node Manager进程都使用DemoTrust.jks keystore中相同的发证机构，该机构已经签署了所有主机上的所有SSL证书，所以这几方都互相信任对方。</P>
<P>　　编辑nodemanager.properties，并指定 <BR>KeyStores=DemoIdentityAndDemoTrust<BR>该文件可以指定的其他选项包括Node Manager进程应该监听的端口和网络接口。我们使用默认值。</P>
<P>　　保存nodemanager.properties文件。</P>
<P>　　4. 将Node Manager注册为Windows Service / UNIX守护程序。（可选）</P>
<P>　　如果正在使用UNIX，那么按照http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html中的步骤进行配置。</P>
<P>　　对于Windows...<BR>- 转到WL_HOME/server/bin<BR>- 检查“BEA WebLogic ... NodeManager”是否被列为Windows Service （在控制面板/管理工具/服务)。<BR>- 如果它被列为服务，并且正在运行，那么使用鼠标右键单击该服务，选择停止，然后运行：<BR>.\uninstallNodeMgrSvc.cmd<BR>, 按F5刷新服务，确认它已经不再服务列表中。<BR>- 运行<BR>.\installNodeMgrSvc.cmd</P>
<P>- 确认“BEA WebLogic ... NodeManager”服务是否被列为Windows Service</P>
<P><BR>　　5. 在主机上安装Managed Servers需要的文件。</P>
<P>　　WebLogic Portal在每个Managed Server根目录中都需要“wsrpKeystore.jks”文件。默认情况下，Node Manager将在WL_HOME/common/nodemanager的子目录中运行服务器，例如，WL_HOME/common/nodemanager/ms1/…。</P>
<P>　　将wsrpKeystore.jks文件从Administration Server主机Administration Server域目录中复制到该主机上的WL_HOME/common/nodemanager/wsrpKeystore.jks。</P>
<P>　　假定您正在生产模式运行，为了使该主机上的Managed Servers能够自动启动，您在WL_HOME/common/nodemanager目录中也将需要一个boot.properties文件。</P>
<P>　　在WL_HOME/common/nodemanager中创建一个“boot.properties”文件。添加以下形式的内容：<BR>　　username=weblogic (或者任何其他系统用户名)<BR>　　password=weblogic (或者任何其他系统密码)</P>
<P>　　保存该文件。</P>
<P>　　对于每台运行Managed Server的主机重复这些步骤。 <BR><BR>　　<B>通过WebLogic Administration Console指定整体Managed Server和Node Manager配置:</B><BR>　　1. 启动Administration Server。这时候不要启动任何Managed Server。</P>
<P>　　2. 连接WebLogic Administration Console。</P>
<P>　　3. 在WebLogic Administration Console中，我们需要指定主机列表（Managed Server应当运行在其中每个主机上）和Administration Server如何联系每台主机上的Node Manager进程。</P>
<P>　　Node Manager需要该信息，因此它知道启动哪个Managed Server以及在哪里启动。该信息能够在Configuration Wizard中的域安装时指定。如果它还没有被配置，或者我们希望修改它，那么这可以通过WebLogic Administration Console进行配置。</P>
<P>　　3a. 在左窗格中，单击“&lt;域&gt;/计算机”。<BR><BR>　　3b. 如果没有列出计算机，那么为每台正在运行Managed Server的主机创建一个计算机项。单击“Configure a new Machine/Unix Machine...”链接指定该信息。为计算机指定一个唯一名称，并单击Create。对于UNIX计算机，您还能够指定Node Manager服务以特定的userID/groupID运行。<BR><BR>　　3c. 单击Node Manager选项卡，然后指定DNS地址，Administration Server将使用该地址访问那台主机上的Node Manager进程。对于该示例，保持默认的Listen Port设置为5555。</P>
<P>　　如前面所提到的，为了使演示SSL配置能够工作，指定用于联系Managed Server的DNS主机地址必须与Managed Server主机的演示SSL证书匹配。</P>
<P>　　对于Windows主机，Managed Server的演示SSL证书通常只是主机名称——例如，如果完整的主机名称是“host1.foo.com”，那么SSL证书将被链接到主机“host1”。在这种情况下，您将希望Administration Serer在连接时使用主机名“host1”s，而不是“host1.foo.com”或IP地址。</P>
<P>　　对于UNIX主机，Managed Server的演示SSL证书通常是完全限定DNS名称——例如，如果完整的主机名是“host5.foo.com”，那么SSL证书将被链接到主机“host5.foo.com”。在这种情况下，您将希望Administration Server在连接时使用主机名“host5.foo.com”，而不是“host5”或IP地址。</P>
<P>　　3d. 单击服务器选项卡，然后指定哪台（些）Managed Server将运行在该机器上，单击Apply。</P>
<P>　　3e. 对所有运行Managed Server的主机重复步骤3。 <BR>　　指定Node Manager应当如何通过WebLogic Administration Console启动和控制每台Managed Server：<BR>在WebLogic Administration Console中，我们需要配置关于每台Managed Server的信息，使Node Manager能够启动它。按照以下步骤配置每个Managed Server节点： </P>
<P>　　1. 在左窗格中，单机&lt;域&gt;/&lt;Managed Server&gt;节点。</P>
<P>　　2. 选中Configuration --&gt; Remote Start选项卡。为了填写该页面，我们需要一些关于Managed Server如何启动的信息。获取这些信息的一个简单方法是通过一个startManagedServer脚本在合适的装箱中手动启动Managed Server。下一步将帮助识别这些信息。</P>
<P>　　3. 为了构建一个startManagedServer脚本，我们暂时在每台运行Managed Server的主机上创建一个“伪造”域文件系统的目录。以后，在我们获得信息后，将会删除这个“伪造”域目录，因为它在运行时是不需要的。</P>
<P>　　在每台运行Managed Server的主机上：（例如，host1.foo.com，host2.foo.com）</P>
<P>　　启动Configuration Wizard创建一个新的WebLogic Configuration，使用Basic WebLogic Portal Domain模板，使用Express配置，输入与真实域相同的用户名和密码，配置与真实域相同的启动模式，命名并创建域。</P>
<P>　　对于该主机上的每个Managed Server： </P>
<P>　　4. 通过startManagedWebLogic脚本启动Managed Server。</P>
<P>　　如前面所提到，为了使演示SSL配置能够工作，在启动Managed Server时指定的Administration Server主机地址必须与Administration Server主机的演示SSL证书匹配。</P>
<P>　　对于Windows主机，Administration Server主机的演示SSL证书通常只是主机名称——例如，如果完整的主机名是“adminbox.foo.com”，那么SSL证书将被链接到主机“adminbox”。在这种情况下，您将希望Managed Server在连接时使用主机名“foobar”，而不是“adminbox.foo.com”或IP地址。</P>
<P>　　对于UNIX主机，Administration Server主机的演示SSL证书通常是完全限定的DNS名称——例如，如果完整主机名是“adminbox.foo.com”，那么SSL证书将被链接到主机“adminbox.foo.com”。在这种情况下，您将希望Managed Server在连接时使用主机名“adminbox.foo.com”，而不是“adminbox”或IP地址。</P>
<P>　　在命令/终端窗口中，转到刚刚创建的“伪造”域目录。</P>
<P>　　通过startManagedWebLogic脚本启动Managed Server。</P>
<P>　　例如，如果Managed Server名为“ms2”，Administration Server在一个具有SSL证书名“adminbox”和端口8001的Windows主机上： <BR>startManagedWebLogic.cmd ms2 t3://adminbox:8001</P>
<P>　　如果您正在生产模式（应该的模式）下运行域，那么您将需要输入系统用户名和密码。</P>
<P>　　一旦Managed Server成功启动，查看WebLogic Administration Console窗口中关于如何启动Managed Server的信息。我们将使用这些信息为Managed Server实例配置Node Manager。如果您是在windows下启动，那么文件和目录名可能会被截短。如果希望，可以扩展路径，但这不是必需的。</P>
<P>　　复制CLASSPATH=... 等行和运行java来启动服务器进程的行。这些行以“weblogic.Server”结束，删除所有新行。</P>
<P>　　例如，CLASSPATH= 等行可能看上去像这样：</P>
<P align=center><IMG height=454 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110512.gif" width=427></P>
<P><BR>　　服务器启动的行可能看上去像这样：</P>
<P align=center><IMG height=301 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110513.gif" width=484></P>
<P>　　5. 关闭Managed Server。</P>
<P>　　6. 回到WebLogic Administration Console，为Managed Server指定Java主目录。Java主目录列在服务器启动行的开始处。删除“bin/java”部分。例如，在上面的服务器启动行中产生Java主目录D:\WL81SP~2\JROCKI~1。输入该值作为Java主目录。</P>
<P>　　7. 现在在主机上指定BEA主目录，也就是WebLogic Platfrom安装的位置。该目录也在CLASSPATH行中被列出了几次，它以weblogic目录开头。在上面的服务器启动行中，您可以看到字符串'D:\WL81SP~2\WEBLOG~1'重复了许多次。删除WebLogic目录，生成'D:\WL81SP~2'。输入该值作为BEA home。</P>
<P>　　8. 保留（Managed Server）根目录字段为空。默认情况下，将在WL_HOME\common\nodemanager的一个子目录下启动Managed Server。</P>
<P>　　9. 现在为Managed Server指定Class Path。使用与服务器启动期间相同的CLASSPATH，但删除前缀“CLASSPATH=”。</P>
<P>　　10. 现在指定（Managed Server）参数。它们由许多服务器启动行组成，去掉了java前缀，删除了-Djava.security.policy= 参数，还删除了weblogic.Server后缀。</P>
<P>　　例如，我们从这样的一行开始：</P>
<P align=center><IMG height=354 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110514.gif" width=366></P>
<P>　　然后删除前缀、参数、后缀，以这样的参数设置结束：</P>
<P align=center><IMG height=165 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110515.gif" width=524></P>
<P>　　11. 指定Security Policy文件。使用与我们在前面步骤中删除的相同的安全策略参数，确定这是绝对路径。不包括-Djava.security.policy=前缀。这将给我们下面的策略文件： <BR>'D:\WL81SP~2\WEBLOG~1\server\lib\weblogic.policy'</P>
<P>　　12. 输入系统用户名和密码，单击Apply </P>
<P>　　13. 为域中每台Managed Server重复这个步骤顺序。</P>
<P>　　在为所有Managed Server完成这些步骤后，关闭Administration Server。 <BR><BR><B>　　使用Node Manager测试</B><BR>　　1. 首先，让我们确保Node Manager进程正运行在每台Managed Server主机上。</P>
<P>　　在Windows下，能够通过控制面板/管理工具/服务启动Node Manager Windows Service。在UNIX下，以合适的方式启动守护程序。</P>
<P>　　确认Node Manager进程保持运行状态至少30秒。如果错误配置Node Manager进程，它有时会立即关闭。</P>
<P>　　2. 如果需要，启动Administration Server。</P>
<P>　　3. 登录进WebLogic Administration Console，并执行希望的操作。</P>
<P>　　例如，为了启动所有的Managed Server，选择&lt;域&gt;/&lt;Clusters&gt;，右键单击您的群集，选中“Start/Stop this Cluster...”。然后单击“Start all Managed Servers...”，单击Yes。WebLogic Administration Console将与每台Managed Server主机上的Node Manager服务通信，并通知它们启动Managed Server。</P>
<P>　　如果一切都工作了，WebLogic Administration Console屏幕将稳定，这时，所有的Managed Server处于RUNNING状态、TASK COMPLETED状态。</P>
<P>　　如果错误发生，状态将变为FAILED。您可以在FAILED文本上单击来查看错误消息。您能够通过单击“Remote Start Output”选项卡发现额外的信息。</P>
<P>　　4. 如果所有东西工作得都很好，您可以删除在每个装箱上创建的“伪造”域目录，因为它不再需要。<BR><BR>　　<B>调试Node Manager配置</B><BR>　　调试Node Manager涉及到检查不同组件记录的各种日志文件。</P>
<P>　　下列组件记录了日志文件：<BR>　　- WebLogic Administration Console客户端（调试从管理客户端到Node Manager进程的通信）。</P>
<P>　　默认情况下调试日志是关闭的，但可以为与一台主机上的特定Node Manager进程通信而启用。</P>
<P>　　为了启动它，登录进WebLogic Administration Console，选择希望在与其通信时启用调试的Machines/&lt;主机&gt;，选中“Debug Enabled”框，单击Apply。</P>
<P>　　日志文件被写入到Administration Server的域目录的NodeManagerClientLogs子目录中。</P>
<P>　　- Node Manager进程（调试启动、监控和控制Managed Server）。</P>
<P>　　该输出可以从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，然后选择Control选项卡，然后选择Remote Start Output子选项卡，单击“View Node Manager output”链接来实现。</P>
<P>　　默认情况下，该日志记录是开启的，它被存储在Node Manager运行的主机上，默认情况下存储在WL_HOME/common/nodemanager/NodeManagerLogs/NodeManagerInternal中。</P>
<P>　　- Managed Server进程（调试服务器启动错误）</P>
<P>　　该日志记录默认时是开启的，以多个文件存储在运行Node Manager的主机上。</P>
<P>　　默认情况下把服务器日志写入到WL_HOME/common/nodemanager/&lt;servername&gt;/&lt;servername&gt;.log。</P>
<P>　　该输出能够从WebLogic Administration Console中通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，然后选择窗口底部“View server log”链接来查看。</P>
<P>　　默认情况下把任何服务器向标准输出写的输出发送到WL_HOME/common/nodemanager/&lt;domainname&gt;_&lt;servername&gt;/&lt;servername&gt;_output.log。</P>
<P>　　该输出能够从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，选择Control选项卡，选择Remote Start Output子选项卡，单击“View Server output”链接来实现。</P>
<P>　　默认情况下把任何由服务器写到标准错误的输出发送到<BR>WL_HOME/common/nodemanager/&lt;domainname&gt;_&lt;servername&gt;/&lt;servername&gt;_error.log。</P>
<P>　　该输出可以从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，选择Control选项卡，选择Remote Start Output子选项卡，然后单击“View Server error output”链接来实现。</P>
<P>　　原文出处：<A href="http://dev2dev.bea.com/products/wlportal81/articles/wlp_roth.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/articles/wlp_roth.jsp</A></P><img src ="http://www.blogjava.net/kapok/aggbug/9735.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-08-10 19:09 <a href="http://www.blogjava.net/kapok/archive/2005/08/10/9735.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>结合使用WebLogic Node Manager Service和WebLogic Portal </title><link>http://www.blogjava.net/kapok/archive/2005/07/09/7424.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sat, 09 Jul 2005 13:51:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/07/09/7424.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/7424.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/07/09/7424.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/7424.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/7424.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/wlportal/2004110503.html">http://dev2dev.bea.com.cn/techdoc/wlportal/2004110503.html</A><BR>WebLogic Node Manager Service为产品环境中的Managed Server提供了管理功能，包括： 
<UL>
<LI>Managed Server的状态监控。 
<LI>Managed Server在进程或状态故障时自动重启。 
<LI>对Managed Server进行远程和集中控制，包括启动和停止服务器。 </LI></UL>
<P>　　Node Manager安全地提供这些功能，将双向的SSL身份验证、一个被允许控制Managed Server的可信主机列表、其他安全功能结合在一起。</P>
<P>　　Node Manager可以注册为Windows服务或UNIX守护程序，因此当机器启动或重启时，它能够自动启动计算机上的Managed Server。</P>
<P>　　本文讨论了如何为WebLogic Portal配置WebLogic Node Manager Service，并提供了一个示例配置顺序。</P>
<P>　　有关Node Manager的完整信息，请参阅<A href="http://e-docs.bea.com/wls/docs81/adminguide/nodemgt.html" target=_blank>http://e-docs.bea.com/wls/docs81/adminguide/nodemgt.html</A>。 <BR>Node Manager概览：<BR><BR>　　在高层次上，Node Manager使用被管理的服务器信息进行配置，并且使这些服务器保持在所希望的状态。</P>
<P>　　Node Manager只管理已经启动的托管服务器——Node Manager不影响手动启动的托管服务器。</P>
<P>　　每一台Managed Server运行的主机上能够运行一个Node Manager进程。Node Manager与该主机上的每个Managed Server保持一个socket连接，并使用该连接监视Managed Server的状态。如果Managed Server进程意料外故障或WebLogic确定该进程活着但处在失败或挂起状态，那么Node Manager能够自动重启Managed Server。 </P>
<P>　　除了自动重启功能，Node Manager还具有远程控制功能，因此管理员能够通过WebLogic Administration Console或WebLogic Admin工具启动或停止单个或所有Managed Server。在每个Managed Server主机上，Node Manager在配置的端口上监听服务器远程控制命令。</P>
<P align=center><IMG height=369 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110506.gif" width=482></P>
<P>　　Node Manager使用几个安全方法来限制对Node Manager Service、以及从Node Manager Service到Managed Server的远程管理访问。</P>
<UL>
<LI>所有从Node Manager Service到Managed Server的通信都是通过安全的双向SSL。每一方必须向另一方证明其身份。 
<LI>所有从管理客户端（WebLogic Administration Console或weblogic Admin工具）到Node Manager Service的通信也都是通过安全的双向SSL。每一方必须向其他一方证明其身份。 
<LI>Node Manager Service配置支持指定一个允许管理客户端连接的受信任主机列表。来自其他（非受信任）主机的连接是不被允许的。该功能能够用于拒绝来自除Administration Server主机之外的访问。 
<LI>也有其他一些安全选项是可用的，例如在一个具有多个网卡的系统上指定Node Manager Service应该监听的网络地址。 </LI></UL>
<P>　　Node Manager Service创建命令行，启动Managed Server进程，不需要调用启动脚本。因此，在启动一台Managed Server时指定标准startManagedServer脚本向操作系统发出的相同类型的信息是很重要的。 <BR><BR>　　结合使用Node Manager和WebLogic Portal：<BR>　　除了标准的Node Manager配置步骤，在配置Node Manager与WebLogic Portal协同工作时需要应用一个额外的步骤。</P>
<P>　　特别地，Administration Server域目录包含一个名为“wrspKeystore.jks”的文件。每个Managed Server需要访问服务器根目录上的一个目录中的该文件。</P>
<P>　　默认情况下，Node Manager将在WL_HOME/common/nodemanager的一个子目录下运行服务器，例如，WL_HOME/common/nodemanager/ms1/…。 </P>
<P>　　因此，如果Node Manager启动了目录WL_HOME/common/nodemanager/ms1/中一个名为ms1的Managed Server ，那么应该把wsrpKeystore.jks复制到每个Managed Server上的一个目录中，例如，WL_HOME/common/nodemanager/wsrpKeystore.jks。 <BR><BR>　　<B>Node Manager配置概述<BR></B>　　存在许多可能的配置变体，这些变体在<A href="http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html" target=_blank>http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html</A>中有详细描述。</P>
<P>　　一些主要的配置变体包括：</P>
<UL>
<LI>Node Manager作为一个系统服务/守护程序还是手动启动的进程运行。 
<LI>安全配置的类型和程度。 
<LI>Node Manager是否应该自动杀死崩溃或运行在故障状态的Managed Server进程。 </LI></UL>
<P>　　如果将Node Manager配置为服务器/守护程序，那么在系统启动时将会自动启动它。以这种方式配置，Node Manager能够在机器重启时自动重启Managed Server。<BR><BR>　　<B>Node Manager配置过程：</B><BR>　　在配置Node Manager中涉及的主要步骤包括：</P>
<UL>
<LI>指定安全配置和受信任主机。 
<LI>在每台Managed Server主机上，必须配置Node Manager进程，可选择注册为服务/守护程序。在该步骤中指定安全设置。 
<LI>在WebLogic Administration Console中，需要指定每台Managed Server运行在哪台主机上，以及Administration Server如何联系每台主机上的Node Manager服务。 
<LI>在WebLogic Administration Console中，Node Manager正在管理的每台Managed Server也必须进行配置，这样Node Manager就能够建立起要发送给操作系统的命令。我们将首先通过startManagedWeglogic脚本启动服务器、注意生成的命令行，并使用该信息正确配置每台Managed Server。 </LI></UL>
<P>　　<B>为WebLogic Portal配置Node Manager示例</B><BR>　　该示例配置用于具有一台Administration Server和两台Managed Server的域。每台服务器将运行在其自己的主机上。</P>
<P>Administration Server:<BR>- platform: Windows 2000<BR>- hostname: adminbox.foo.com<BR>- IP address: 192.168.1.51</P>
<P>Managed Server 1 (ms1):<BR>- platform: Windows 2000<BR>- hostname: host1.foo.com<BR>- IP address: 192.168.1.52</P>
<P>Managed Server 2 (ms2):<BR>- platform: Windows 2000<BR>- hostname: host2.foo.com<BR>- IP address: 192.168.1.53</P>
<P><B>　　选择安全配置和信任机制</B><BR>　　该示例产生环境配置使用完整安全模式。</P>
<P>　　我们将使用WebLogic演示SSL身份keystore (DemoIdentity.jks)和演示SSL发证机构（CA）信任keystore (DemoTrust.jks)，它们包含在WebLogic安装中，安装在WL_HOME/server/lib/*.jks。</P>
<P>　　身份keystore保存证明一方身份的SSL证书；信任keystore保存受信任发证机构的SSL证书，发证机构能够用于证实身份。运行时，服务器提供SSL证书，客户端根据列在其信任keystore中的CA进行检查。</P>
<P>　　SSL证书也包含一个主机身份，该身份嵌入在证书的“Subject”（主题）字段中。SSL客户端使用该主机身份和证书/信任发证机构匹配项确定远程服务器的证书是否可用。客户端验证两台服务器的身份证书都能够被客户端的信任发证机构所接受，客户端用于联系服务器的主机地址与列在证书中的主机身份匹配。</P>
<P>　　每台安装WebLogic的主机具有自己惟一的特定于主机的演示SSL证书，该证书与那台特定的主机的身份一起嵌入。该证书被嵌入到DemoIdentity.jks keystore中。主机身份能够通过Java keytool实用程序显示。我们后面将看一个例子。所有的演示SSL证书由同一个DemoTrust.jks信任发证机构签署，该机构对于所有主机都是一样的。</P>
<P>　　和双向SSL一起使用这些演示keystore，客户端将在DemoIdentity.jks中向一个服务器提供SSL证书，服务器将检查列在其DemoTrust.jks keystore中的CA SSL证书是否接受该证书。</P>
<P>　　这些演示keystores不提供安全的环境。每个WebLogic安装包括相同的演示 CA信任keystore文件（在WL_HOME/server/lib/DemoTrust.jks中）和生成DemoTrust.jks keystore中CA签署的特定于主机的演示身份证书的工具。因此，任何WebLogic安装都能产生被其他使用该DemoTrust.jks CA keystore的WebLogic安装所接受。</P>
<P>　　因此，对于一个真实的生产环境，您应该希望禁用演示CA信任keystore (DemoTrust.jks)，并使用一个有名的发证机构签署的SSL证书，或者至少是您自己定制的发证机构。有关更多信息，请参见<A href="http://e-docs.bea.com/wls/docs81/secmanage/ssl.html" target=_blank>http://e-docs.bea.com/wls/docs81/secmanage/ssl.html</A>。</P>
<P>　　因为SSL证书包含主机身份信息，所以当使用SSL证书时，特别是演示证书时，应当注意客户端用于联系服务器的主机名称必须和服务器证书中的主机身份匹配。</P>
<P>　　这暗示多数情况下主机名称是在一个域中配置的。它也暗示了DNS命名。</P>
<P>　　一个通用规则是：不要使用IP地址，而是使用主机名称。主机名称需要以正确格式指定。在指定一个主机名称时，当指定服务器地址时会为SSL证书主机名称格式使用接收服务器端的规则。例如，一个客户端使用的地址“fooHost”需要与服务器SSL证书主机身份字段匹配（如“fooHost”）。如果客户端正在使用“fooHost.foo.com”，服务器SSL证书的主机身份字段是“fooHost”，那么SSL连接将会失败。</P>
<P>　　例如，当一台Administration Server联系一个Managed Server时，为了使SSL配置工作，Administration Server用于联系Managed Server的主机地址必须与Managed Server主机的SSL证书主机身份匹配。</P>
<P>　　使用“keytool” Java实用程序，您能够检查嵌入在演示SSL证书中的主机名称身份，该证书嵌入在DemoIdentity.jks中。这里显示了如何做到这一点：</P>
<P>转到WL_HOME/server/bin。<BR>setWLSEnv.cmd/.sh<BR>cd ..\lib <BR>(WL_HOME/server/lib)<BR>keytool -list -v -alias demoidentity -keystore DemoIdentity.jks<BR>当提示输入keystore密码时，输入： <BR>DemoIdentityKeyStorePassPhrase<BR><BR>　　在输出（Owner字段）中、CN属性是嵌入到主机名称身份中的。</P>
<P>　　例如，在名为“host1.foo.com”的Windows主机中，您将看到像下面的输出：<BR><BR>D:\wl81sp3\weblogic81\server\lib&gt;keytool -v -alias demoidentity <BR>-list -keystore DemoIdentity.jks<BR>Enter keystore password: DemoIdentityKeyStorePassPhrase<BR>Alias name: demoidentity<BR>Creation date: Jun 24, 2004<BR>Entry type: keyEntry<BR>Certificate chain length: 1<BR>Certificate[1]:<BR>Owner: CN=host1, OU=FOR TESTING ONLY, O=MyOrganization, <BR>L=MyTown, ST=MyState, C=US<BR>Issuer: CN=CertGenCAB, OU=FOR TESTING ONLY, O=MyOrganization, <BR>L=MyTown, ST=MyState, C=US<BR>Serial number: -3cb71787b0af00ee4160180a08e071b0<BR>Valid from: Wed Jun 23 16:32:25 MDT 2004 until: Mon Jun 24 16:32:25 MDT 2019<BR>Certificate fingerprints:<BR>　　MD5: 34:B4:D4:86:28:06:9B:C2:29:A1:09:4B:A1:9C:77:55<BR>　　SHA1: F8:A0:AE:6C:A7:9B:CC:FD:0D:AE:17:8D:D9:C8:2F:E9:36:41:86:5B</P>
<P><BR>　　在这种情况下，SSL证书具有主机身份“host1”，因此无论什么时候客户端需要联系host1.foo.com，它需要指定“host1”。</P>
<P>　　对于Windows主机，嵌入在主机上DemoIdentity.jks中的演示SSL证书通常只是主机名称——例如，如果完整的主机名称是“host1.foo.com”，那么将把SSL证书链接到主机“host1”。在这种情况下，您将希望任何联系该Windows主机的客户端在连接时都使用DNS地址“host1”。</P>
<P>　　对于UNIX主机，嵌入在主机上DemoIdentity.jks中的演示SSL证书通常是完全限定的DNS名称——例如，如果完整的主机名称是“host5.foo.com”，那么将会把SSL证书链接到主机“host5.foo.com”。在这种情况下，您将希望任何联系该UNIX主机的客户端在连接时使用主机名称“host5.foo.com”，而不是“host5”或IP地址。</P>
<P>　　当一台Managed Server联系Administration Server时，完全相同的规则也适用。</P>
<P>　　就主机信任来说，我们将允许只有Administration Server主机能够联系Node Manager Service进程。这将允许我们从该Administration Serer主机使用WebLogic Administration Console或weblogic.Admin工具来控制Managed Servers，但拒绝来自任何其他主机的访问。 <BR><BR><B>　　在每台Managed Server主机上配置Node Manager进程：</B><BR>　　在每台运行着至少一个Managed Server的主机上执行这些步骤一次。在我们的示例配置域中，这些步骤将在主机host1.foo.com和host2.foo.com上执行。</P>
<P>　　1. 首先，让我们手动启动Node Manager，它将创建一些用于编辑的配置文件。默认情况下，Node Manager将在第一次启动时在目录WL_HOME/common/nodemanager下编写一些配置文件。 <BR>打开一个命令/终端窗口，cd WL_HOME/server/bin<BR>运行 “startNodeManager(.cmd/.sh)”来交互地启动Node Manager。 <BR>Node Manager将在WL_HOME/common/nodemanager中创建一些文件。<BR>在WebLogic Administration Console窗口中，等待直到看到一些下面形式的文本： </P>
<P>　　&lt;Jul 26, 2004 6:00:18 PM MDT&gt; &lt;Info&gt; &lt;NodeManager@*.*:5555&gt; &lt;NodeManager started, log messages being written into file ..., </P>
<P>　　然后输入ctrl-C关闭Node Manager。</P>
<P>　　2. 在WL_HOME/common/nodemanager/nodemanager.hosts中编辑受信任主机文件，以便指定被允许访问和控制将要运行在该主机上的Node Manager Service的远程主机集。每台受信任主机应该在单独一行输入。</P>
<P>　　您能够以主机名称或IP地址输入受信任主机。如果输入了主机名称，那么需要在nodemanager.properties文件中启用逆向DNS查找，我们将在下一步配置该文件。<BR>如果您希望Node Manager Service接受来自任何主机的远程命令，就在hosts文件中输入*。</P>
<P>　　在该示例中，我们希望限制只有Administration Server主机能够访问，因此我们输入Administration Server IP地址：192.168.1.51。</P>
<P>　　限制对受信任主机文件WL_HOME/common/nodemanager/nodemanager.hosts的访问。Node Manager Service在运行期间读取该文件，如果在该文件中添加了主机，它将动态启动接收来自那些主机的远程管理命令。因此，限制对该文件的访问很重要。</P>
<P>　　3. 编辑Node Manager 属性文件WL_HOME/common/nodemanager/nodemanager.properties，来指定SSL安全配置和其他选项。</P>
<P>　　这是指定SSL安全身份和信任keystore与访问信息（如别名和passphrase）的地方。</P>
<P>　　在运行时，Node Manager进程将为主机上的一台或多台Managed Server、可能也有Administration Server或weblogic.Admin工具提供一个SSL证书。所有这几方都将需要在信任keystore中拥有一个发证机构，该keystore接收提供的Node Manager SSL证书。另外，所有这几方都将在运行时向Node Manager进程提供它们自己的SSL证书。因此，Node Manager进程将需要在信任keystore中具有一个发证机构，该keystore接受这几方提供的SSL证书。</P>
<P>　　为了本示例的目的，我们将配置Node Manager进程使用DemoIdentity.jks keystore和DemoTrust.jks CA keystore。因为Administration Server、Managed Server和Node Manager进程都使用DemoTrust.jks keystore中相同的发证机构，该机构已经签署了所有主机上的所有SSL证书，所以这几方都互相信任对方。</P>
<P>　　编辑nodemanager.properties，并指定 <BR>KeyStores=DemoIdentityAndDemoTrust<BR>该文件可以指定的其他选项包括Node Manager进程应该监听的端口和网络接口。我们使用默认值。</P>
<P>　　保存nodemanager.properties文件。</P>
<P>　　4. 将Node Manager注册为Windows Service / UNIX守护程序。（可选）</P>
<P>　　如果正在使用UNIX，那么按照http://e-docs.bea.com/wls/docs81/adminguide/confignodemgr.html中的步骤进行配置。</P>
<P>　　对于Windows...<BR>- 转到WL_HOME/server/bin<BR>- 检查“BEA WebLogic ... NodeManager”是否被列为Windows Service （在控制面板/管理工具/服务)。<BR>- 如果它被列为服务，并且正在运行，那么使用鼠标右键单击该服务，选择停止，然后运行：<BR>.\uninstallNodeMgrSvc.cmd<BR>, 按F5刷新服务，确认它已经不再服务列表中。<BR>- 运行<BR>.\installNodeMgrSvc.cmd</P>
<P>- 确认“BEA WebLogic ... NodeManager”服务是否被列为Windows Service</P>
<P><BR>　　5. 在主机上安装Managed Servers需要的文件。</P>
<P>　　WebLogic Portal在每个Managed Server根目录中都需要“wsrpKeystore.jks”文件。默认情况下，Node Manager将在WL_HOME/common/nodemanager的子目录中运行服务器，例如，WL_HOME/common/nodemanager/ms1/…。</P>
<P>　　将wsrpKeystore.jks文件从Administration Server主机Administration Server域目录中复制到该主机上的WL_HOME/common/nodemanager/wsrpKeystore.jks。</P>
<P>　　假定您正在生产模式运行，为了使该主机上的Managed Servers能够自动启动，您在WL_HOME/common/nodemanager目录中也将需要一个boot.properties文件。</P>
<P>　　在WL_HOME/common/nodemanager中创建一个“boot.properties”文件。添加以下形式的内容：<BR>　　username=weblogic (或者任何其他系统用户名)<BR>　　password=weblogic (或者任何其他系统密码)</P>
<P>　　保存该文件。</P>
<P>　　对于每台运行Managed Server的主机重复这些步骤。 <BR><BR>　　<B>通过WebLogic Administration Console指定整体Managed Server和Node Manager配置:</B><BR>　　1. 启动Administration Server。这时候不要启动任何Managed Server。</P>
<P>　　2. 连接WebLogic Administration Console。</P>
<P>　　3. 在WebLogic Administration Console中，我们需要指定主机列表（Managed Server应当运行在其中每个主机上）和Administration Server如何联系每台主机上的Node Manager进程。</P>
<P>　　Node Manager需要该信息，因此它知道启动哪个Managed Server以及在哪里启动。该信息能够在Configuration Wizard中的域安装时指定。如果它还没有被配置，或者我们希望修改它，那么这可以通过WebLogic Administration Console进行配置。</P>
<P>　　3a. 在左窗格中，单击“&lt;域&gt;/计算机”。<BR><BR>　　3b. 如果没有列出计算机，那么为每台正在运行Managed Server的主机创建一个计算机项。单击“Configure a new Machine/Unix Machine...”链接指定该信息。为计算机指定一个唯一名称，并单击Create。对于UNIX计算机，您还能够指定Node Manager服务以特定的userID/groupID运行。<BR><BR>　　3c. 单击Node Manager选项卡，然后指定DNS地址，Administration Server将使用该地址访问那台主机上的Node Manager进程。对于该示例，保持默认的Listen Port设置为5555。</P>
<P>　　如前面所提到的，为了使演示SSL配置能够工作，指定用于联系Managed Server的DNS主机地址必须与Managed Server主机的演示SSL证书匹配。</P>
<P>　　对于Windows主机，Managed Server的演示SSL证书通常只是主机名称——例如，如果完整的主机名称是“host1.foo.com”，那么SSL证书将被链接到主机“host1”。在这种情况下，您将希望Administration Serer在连接时使用主机名“host1”s，而不是“host1.foo.com”或IP地址。</P>
<P>　　对于UNIX主机，Managed Server的演示SSL证书通常是完全限定DNS名称——例如，如果完整的主机名是“host5.foo.com”，那么SSL证书将被链接到主机“host5.foo.com”。在这种情况下，您将希望Administration Server在连接时使用主机名“host5.foo.com”，而不是“host5”或IP地址。</P>
<P>　　3d. 单击服务器选项卡，然后指定哪台（些）Managed Server将运行在该机器上，单击Apply。</P>
<P>　　3e. 对所有运行Managed Server的主机重复步骤3。 <BR>　　指定Node Manager应当如何通过WebLogic Administration Console启动和控制每台Managed Server：<BR>在WebLogic Administration Console中，我们需要配置关于每台Managed Server的信息，使Node Manager能够启动它。按照以下步骤配置每个Managed Server节点： </P>
<P>　　1. 在左窗格中，单机&lt;域&gt;/&lt;Managed Server&gt;节点。</P>
<P>　　2. 选中Configuration --&gt; Remote Start选项卡。为了填写该页面，我们需要一些关于Managed Server如何启动的信息。获取这些信息的一个简单方法是通过一个startManagedServer脚本在合适的装箱中手动启动Managed Server。下一步将帮助识别这些信息。</P>
<P>　　3. 为了构建一个startManagedServer脚本，我们暂时在每台运行Managed Server的主机上创建一个“伪造”域文件系统的目录。以后，在我们获得信息后，将会删除这个“伪造”域目录，因为它在运行时是不需要的。</P>
<P>　　在每台运行Managed Server的主机上：（例如，host1.foo.com，host2.foo.com）</P>
<P>　　启动Configuration Wizard创建一个新的WebLogic Configuration，使用Basic WebLogic Portal Domain模板，使用Express配置，输入与真实域相同的用户名和密码，配置与真实域相同的启动模式，命名并创建域。</P>
<P>　　对于该主机上的每个Managed Server： </P>
<P>　　4. 通过startManagedWebLogic脚本启动Managed Server。</P>
<P>　　如前面所提到，为了使演示SSL配置能够工作，在启动Managed Server时指定的Administration Server主机地址必须与Administration Server主机的演示SSL证书匹配。</P>
<P>　　对于Windows主机，Administration Server主机的演示SSL证书通常只是主机名称——例如，如果完整的主机名是“adminbox.foo.com”，那么SSL证书将被链接到主机“adminbox”。在这种情况下，您将希望Managed Server在连接时使用主机名“foobar”，而不是“adminbox.foo.com”或IP地址。</P>
<P>　　对于UNIX主机，Administration Server主机的演示SSL证书通常是完全限定的DNS名称——例如，如果完整主机名是“adminbox.foo.com”，那么SSL证书将被链接到主机“adminbox.foo.com”。在这种情况下，您将希望Managed Server在连接时使用主机名“adminbox.foo.com”，而不是“adminbox”或IP地址。</P>
<P>　　在命令/终端窗口中，转到刚刚创建的“伪造”域目录。</P>
<P>　　通过startManagedWebLogic脚本启动Managed Server。</P>
<P>　　例如，如果Managed Server名为“ms2”，Administration Server在一个具有SSL证书名“adminbox”和端口8001的Windows主机上： <BR>startManagedWebLogic.cmd ms2 t3://adminbox:8001</P>
<P>　　如果您正在生产模式（应该的模式）下运行域，那么您将需要输入系统用户名和密码。</P>
<P>　　一旦Managed Server成功启动，查看WebLogic Administration Console窗口中关于如何启动Managed Server的信息。我们将使用这些信息为Managed Server实例配置Node Manager。如果您是在windows下启动，那么文件和目录名可能会被截短。如果希望，可以扩展路径，但这不是必需的。</P>
<P>　　复制CLASSPATH=... 等行和运行java来启动服务器进程的行。这些行以“weblogic.Server”结束，删除所有新行。</P>
<P>　　例如，CLASSPATH= 等行可能看上去像这样：</P>
<P align=center><IMG height=454 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110512.gif" width=427></P>
<P><BR>　　服务器启动的行可能看上去像这样：</P>
<P align=center><IMG height=301 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110513.gif" width=484></P>
<P>　　5. 关闭Managed Server。</P>
<P>　　6. 回到WebLogic Administration Console，为Managed Server指定Java主目录。Java主目录列在服务器启动行的开始处。删除“bin/java”部分。例如，在上面的服务器启动行中产生Java主目录D:\WL81SP~2\JROCKI~1。输入该值作为Java主目录。</P>
<P>　　7. 现在在主机上指定BEA主目录，也就是WebLogic Platfrom安装的位置。该目录也在CLASSPATH行中被列出了几次，它以weblogic目录开头。在上面的服务器启动行中，您可以看到字符串'D:\WL81SP~2\WEBLOG~1'重复了许多次。删除WebLogic目录，生成'D:\WL81SP~2'。输入该值作为BEA home。</P>
<P>　　8. 保留（Managed Server）根目录字段为空。默认情况下，将在WL_HOME\common\nodemanager的一个子目录下启动Managed Server。</P>
<P>　　9. 现在为Managed Server指定Class Path。使用与服务器启动期间相同的CLASSPATH，但删除前缀“CLASSPATH=”。</P>
<P>　　10. 现在指定（Managed Server）参数。它们由许多服务器启动行组成，去掉了java前缀，删除了-Djava.security.policy= 参数，还删除了weblogic.Server后缀。</P>
<P>　　例如，我们从这样的一行开始：</P>
<P align=center><IMG height=354 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110514.gif" width=366></P>
<P>　　然后删除前缀、参数、后缀，以这样的参数设置结束：</P>
<P align=center><IMG height=165 src="http://dev2dev.bea.com.cn/techdoc/wlportal/image2004110515.gif" width=524></P>
<P>　　11. 指定Security Policy文件。使用与我们在前面步骤中删除的相同的安全策略参数，确定这是绝对路径。不包括-Djava.security.policy=前缀。这将给我们下面的策略文件： <BR>'D:\WL81SP~2\WEBLOG~1\server\lib\weblogic.policy'</P>
<P>　　12. 输入系统用户名和密码，单击Apply </P>
<P>　　13. 为域中每台Managed Server重复这个步骤顺序。</P>
<P>　　在为所有Managed Server完成这些步骤后，关闭Administration Server。 <BR><BR><B>　　使用Node Manager测试</B><BR>　　1. 首先，让我们确保Node Manager进程正运行在每台Managed Server主机上。</P>
<P>　　在Windows下，能够通过控制面板/管理工具/服务启动Node Manager Windows Service。在UNIX下，以合适的方式启动守护程序。</P>
<P>　　确认Node Manager进程保持运行状态至少30秒。如果错误配置Node Manager进程，它有时会立即关闭。</P>
<P>　　2. 如果需要，启动Administration Server。</P>
<P>　　3. 登录进WebLogic Administration Console，并执行希望的操作。</P>
<P>　　例如，为了启动所有的Managed Server，选择&lt;域&gt;/&lt;Clusters&gt;，右键单击您的群集，选中“Start/Stop this Cluster...”。然后单击“Start all Managed Servers...”，单击Yes。WebLogic Administration Console将与每台Managed Server主机上的Node Manager服务通信，并通知它们启动Managed Server。</P>
<P>　　如果一切都工作了，WebLogic Administration Console屏幕将稳定，这时，所有的Managed Server处于RUNNING状态、TASK COMPLETED状态。</P>
<P>　　如果错误发生，状态将变为FAILED。您可以在FAILED文本上单击来查看错误消息。您能够通过单击“Remote Start Output”选项卡发现额外的信息。</P>
<P>　　4. 如果所有东西工作得都很好，您可以删除在每个装箱上创建的“伪造”域目录，因为它不再需要。<BR><BR>　　<B>调试Node Manager配置</B><BR>　　调试Node Manager涉及到检查不同组件记录的各种日志文件。</P>
<P>　　下列组件记录了日志文件：<BR>　　- WebLogic Administration Console客户端（调试从管理客户端到Node Manager进程的通信）。</P>
<P>　　默认情况下调试日志是关闭的，但可以为与一台主机上的特定Node Manager进程通信而启用。</P>
<P>　　为了启动它，登录进WebLogic Administration Console，选择希望在与其通信时启用调试的Machines/&lt;主机&gt;，选中“Debug Enabled”框，单击Apply。</P>
<P>　　日志文件被写入到Administration Server的域目录的NodeManagerClientLogs子目录中。</P>
<P>　　- Node Manager进程（调试启动、监控和控制Managed Server）。</P>
<P>　　该输出可以从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，然后选择Control选项卡，然后选择Remote Start Output子选项卡，单击“View Node Manager output”链接来实现。</P>
<P>　　默认情况下，该日志记录是开启的，它被存储在Node Manager运行的主机上，默认情况下存储在WL_HOME/common/nodemanager/NodeManagerLogs/NodeManagerInternal中。</P>
<P>　　- Managed Server进程（调试服务器启动错误）</P>
<P>　　该日志记录默认时是开启的，以多个文件存储在运行Node Manager的主机上。</P>
<P>　　默认情况下把服务器日志写入到WL_HOME/common/nodemanager/&lt;servername&gt;/&lt;servername&gt;.log。</P>
<P>　　该输出能够从WebLogic Administration Console中通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，然后选择窗口底部“View server log”链接来查看。</P>
<P>　　默认情况下把任何服务器向标准输出写的输出发送到WL_HOME/common/nodemanager/&lt;domainname&gt;_&lt;servername&gt;/&lt;servername&gt;_output.log。</P>
<P>　　该输出能够从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，选择Control选项卡，选择Remote Start Output子选项卡，单击“View Server output”链接来实现。</P>
<P>　　默认情况下把任何由服务器写到标准错误的输出发送到<BR>WL_HOME/common/nodemanager/&lt;domainname&gt;_&lt;servername&gt;/&lt;servername&gt;_error.log。</P>
<P>　　该输出可以从WebLogic Administration Console中查看，这可以通过单击&lt;Domain&gt;/Servers/&lt;ServerName&gt;，选择Control选项卡，选择Remote Start Output子选项卡，然后单击“View Server error output”链接来实现。</P>
<P>　　原文出处：<A href="http://dev2dev.bea.com/products/wlportal81/articles/wlp_roth.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/articles/wlp_roth.jsp</A></P><img src ="http://www.blogjava.net/kapok/aggbug/7424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-07-09 21:51 <a href="http://www.blogjava.net/kapok/archive/2005/07/09/7424.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现电子商务的可伸缩性和高可用性--BEA WebLogic© Server中的集群 </title><link>http://www.blogjava.net/kapok/archive/2005/07/09/7423.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sat, 09 Jul 2005 12:52:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/07/09/7423.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/7423.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/07/09/7423.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/7423.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/7423.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html">http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html</A><A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#1"><BR><BR>集群BEA WebLogic Server </A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#2">访问BEA WebLogic Server 集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#3">DNS负载均衡技术</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#4">代理服务器</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#5">使用BEA WebLogic Server作为Web服务器</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#6">使用BEA WebLogic Server作为负载均衡代理</A> <BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#7">与第三方Web服务器集成</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#8">硬件负载均衡器</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#9">WAN集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#10">Web集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#11">用代理Web服务器的故障切换</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#12">用负载均衡硬件的故障切换</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#13">对象集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#14">无状态会话EJB</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#15">有状态会话EJB</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#16">实体bean</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#17">全部集群的JNDI树</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#18">JMS集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#19">JDBC元池（metapool）集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#20">BEA WebLogic Server实例如何在网络上通信</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#21">配置和管理BEA WebLogic 集群</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#22">配置集群</A> <BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#23">用节点管理器（Node Manager）自动管理</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#24">结束语</A><BR>　　<A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#25">BEA简介</A>
<P><B>摘要<BR></B>　　BEA WebLogic Server™通过它的无与伦比的集群技术为电子商务提供所需的可伸缩性和24×7的可用性。集群指的是：在业务关键型系统内，为了分配负载和消除单点故障而将服务器分组的若干方法。集群实现冗余，通过Web技术和对象集群自动将故障切换到备份服务器或者对象实例、保持有状态对象的状态，以及更新JDNI树和复制感知的占位程序。如果硬件或者软件出现故障，客户机访问被透明地转移到工作的服务器或者对象的复制品。集群对它的客户机而言，就像是单一的“超级”服务器，通过惟一的URL访问。在集群内部，服务器通过IP多播通信，通过DNS负载均衡、硬件负载均衡器或者代理服务器支持简单的Web访问模型。BEA WebLogic集群技术是该行业内最先进的，它为客户提供最高级别的可伸缩性和业务关键型Web应用程序的可靠性。<BR><BR><B>概述</B><BR>　　Internet已经提高了可靠性标准。用户愈加期盼“dial tone”式的服务质量，24×7的可用性和即时性。人们无法接受直接影响业务关键型客户或者业务合作伙伴的故障。基础架构必须保证高可靠性，以便向企业提供不间断的服务。<BR>　　在当今动态的业务环境中，企业必须能够动态地提高能力以满足需求。支持这种应用程序的基础结构必须具备高度可伸缩性，在不必改变硬件或者软件的情况下，就能够无限制的增长。<BR>　　现在，企业使用Java应用服务器，特别是占据领先地位的BEA WebLogic Server，来为客户的自服务、供应链和分配信道管理、交易、转帐、物资供应和许多其他服务开发和部署高度可伸缩和极其可靠的任务关键型应用程序。在BEA WebLogic Server上的部署覆盖了所有行业，包括电讯（AT&amp;T、Qwest和Verizon）、卫生保健（Aetna、Blue Cross/Blue Shield和Oxford Health Plan）、运输（United Airline、Delta Air Line和DHL Worldwide Express）和金融服务（Charles Schwab、American Express和Fidelity Investment）。</P>
<P>　　这些成功的客户使BEA成为基础架构软件的领头羊，它拥有2100多个合作伙伴ISV、ASP和系统集成商，以及11000多家客户。BEA WebLogic是行业内最先进的Java应用服务器，一直赢得最高的市场份额，以及对J2EE和Web service技术的大力支持。<BR>　　BEA WebLogic Server成功的关键之一是它已经证明，通过卓越的集群能力，它能够提供可伸缩性和高可用性。<A></A><A id=1 name=1></A></P>
<P><B>集群BEA WebLogic Server <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR>　　</B>BEA WebLogic Server集群由许多部署在网络上的BEA WebLogic Server组成。集群服务器一起工作，提供比单台服务器功能更强大、可靠性更高的应用程序平台。集群对于其客户机来讲是单台服务器，但事实上，它是起着一台“超级”服务器作用的一组服务器。</P>
<P>与单一服务器相比，BEA WebLogic集群有两个主要优点：</P>
<UL>
<LI>可伸缩性——集群的能力不受某一单台服务器或者单台机器的限制。为了提高处理能力，可以动态地将新的服务器添加到集群中。如果需要更多的硬件，可以在新机器上添加新的服务器。如果单一的服务器不能充分利用现有的机器，可以在该机器上添加其他的服务器。 
<LI>高可用性——集群利用多台服务器的冗余使客户机与硬件或者软件故障隔离开。集群中的多台服务器可以提供相同的服务。如果某台服务器发生故障，另一台服务器可以接管。从发生故障的服务器自动切换到正在运行的服务器的能力，可以保证对客户机而言具有应用程序的无缝可用性，而无需客户机知道出现了问题。 </LI></UL>
<P><B>集群的其他特性：</B></P>
<TABLE cellSpacing=1 cellPadding=4 width="100%" bgColor=#000000 border=0>
<TBODY>
<TR bgColor=#ffffff>
<TD width="18%">特性</TD>
<TD width="82%">好处</TD></TR>
<TR bgColor=#ffffff>
<TD>没有扩展瓶颈</TD>
<TD>根据需要，可以很容易并且动态地将新服务器添加到配置中，以满足日益增加的用户需求。此外，全部的请求负载在服务器间分配，以便保持充分利用资源。</TD></TR>
<TR bgColor=#ffffff>
<TD>单点故障不影响可用性</TD>
<TD>请求自动地从失效的组件转移到工作的组件。此外，还保护会话状态以保证完全向用户和应用程序隐藏所发生的所有故障（例如服务器崩溃）。</TD></TR>
<TR bgColor=#ffffff>
<TD>对应用程序和开发人员的透明性</TD>
<TD>程序员不应该处理复杂的复制、请求路由、负载均衡以及故障切换等。客户可以购买现成的应用程序组件，这些组件可以透明地部署，而不用修改已集群的应用程序服务器。</TD></TR>
<TR bgColor=#ffffff>
<TD>硬件和操作系统的无关性</TD>
<TD>BEA WebLogic Server集群可以在多种平台上运行 。由于与专用的平台特性无关，所以BEA WebLogic集群可以由各种系统组成，从运行Microsoft Windows NT的Intel机器一直到大规模Unix多处理器和IBM OS/390大型机。</TD></TR></TBODY></TABLE>
<P><B>　　</B>BEA WebLogic Server集群可以进一步聚合在某个域内——域指的是能够由控制台作为单一的逻辑安装所管理的一组BEA WebLogic Server实例和/或者集群。如上所述，BEA WebLogic Server集群以对应用程序开发人员透明的方式向J2EE应用程序提供可伸缩性和高可用性。但是，为了使应用程序程序员和管理员最大限度地提高其应用程序的可伸缩性和可用性，让他们了解集群内固有的问题很重要。<BR><B>　　</B>BEA WebLogic Server有几种形式的复杂的集群，分别是Web集群、对象集群、JMS（Java消息服务）集群和JDBC连接池（元池）集群。<BR><B>　　</B>除了基本的集群功能之外，集群内的BEA WebLogic Server实例可以与全局共享和复制的（全部集群）JNDI树（Java命名和目录接口）的组合相互配合，并在内存中进行Servlet和EJB会话状态的复制。<A id=2 name=2></A></P>
<P><B>访问BEA WebLogic Server集群 <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR>　　</B>BEA WebLogic Server集群在客户机看来是单一的服务器，可以使用简单的URL来寻址。 实际上，该URL必须映射到集群内的多台服务器，以保证连接请求可以在该集群内负载均衡，并且可以透明地进行故障切换。BEA WebLogic Server支持几种实现这种简单访问模型的技术：</P>
<UL>
<LI>DNS负载均衡 
<LI>代理服务器（BEA WebLogic代理，或者使用BEA WebLogic插件的第三方Web服务器) 
<LI>硬件负载均衡器<A id=3 name=3></A> </LI></UL>
<P><B>DNS负载均衡</B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>访问BEA WebLogic Server集群最简单的方法是使用映射到所有集群服务器的IP地址的单一DNS（域名系统）名称。当一个DNS名称映射到多个地址时，DNS服务器将循环使用该列表，连续查找这个DNS名称。这提供了一种简单的负载均衡和故障切换方式。每次客户机解析URL，它都将得到循环的下一个地址。这样可以保证客户机连接均匀地在集群中得到均衡。如果某一客户机请求失败，客户机可以通过再一次查找该名称来故障切换该请求，并用新地址重新尝试。对某些应用程序而言，这是一种简单但足以解决问题的方法。但是，它不具备其他解决方案可提供的性能和可管理水平。<A id=4 name=4></A></P>
<P><B>代理服务器</B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>访问集群的另一种方法是使用代理回该集群的其他Web服务器。代理服务器可以是含有BEA WebLogic Server的Web服务器，或者是具有BEA WebLogic 插件的Apache、Netscape或者Microsoft Web Server。<BR><B>　　</B>代理服务器被设置为将某些类型的请求重定向到支持它的服务器上。例如，可以配置代理服务器处理静态HTML页面请求，而将针对Servlet和Java Server Page的请求重定向到支持代理的BEA WebLogic集群。<BR><B>　　</B>代理服务器的作用类似于硬件负载均衡器，因为代理服务器执行负载均衡、在支持它的集群中的多台服务器之间分配请求。当会话建立时，它继续代理该会话的所有请求到单一的服务器。如果该服务器发生故障，它将任务切换到副服务器。<A id=5 name=5></A></P>
<P><B>使用 BEA WebLogic Server作为 Web Server</B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>BEA在BEA WebLogic Server中加进了具有完全功能的HTTP服务器。因此，BEA WebLogic Server能够作为完全集群解决方案中的主Web服务器。作为Web站点，除继续提供静态HTML文件之外，还提供个性化的、动态的内容，人们可能期望看到更多的Web服务器被Web应用服务器所替代。</P>
<P><B>　　</B>用作Web服务器时，高性能的BEA WebLogic Server支持最新的J2EE 1.3标准，进而为客户提供了以下好处：</P>
<UL>
<LI>与大多数负载均衡硬件，以及最为广泛使用的Web服务器（使用了所提供的插件）集成 
<LI>与大多数Java虚拟机兼容 
<LI>可伸缩性和高可用性的集群 
<LI>跨集群的Servlet状态复制 
<LI>Servlet自动故障切换 
<LI>支持Cookies 
<LI>Servlet转发和链接 
<LI>JSP结果缓冲 
<LI>JSP标签库 
<LI>动态部署 
<LI>基于Web的管理和监控<A id=6 name=6></A> </LI></UL>
<P><B>使用 BEA WebLogic Server作为负载均衡代理</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>BEA WebLogic Server可以作为负载均衡代理与BEA WebLogic 集群接合。就这一点而论，它可以提供负载分配、故障切换和其他的负载均衡器特性，以及处理Web请求的某些部分（例如，快速静态页面服务）。作为代理，然后它可能将大多数请求重定向到支持它的集群。<BR><B>　　</B>图1显示了使用BEA WebLogic代理作为负载均衡器的第一种可能的BEA WebLogic集群配置。这种配置充分利用BEA WebLogic作为Web服务器。</P>
<P align=center><IMG height=402 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022805.gif" width=600><BR>图1：BEA WebLogic代理作为BEA WebLogic Web Serve前面的负载均衡器<A id=7 name=7></A></P>
<P><B>与第三方Web服务器集成</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>可以将BEA WebLogic Server与第三方Web服务器集成。Web服务器代理向使用BEA WebLogic Server 所供插件的BEA WebLogic Server代理请求。插件是为iPlanet (Netscape) Enterprise Server、Microsoft IIS和在指定平台上的Apache准备的。它设计用于第三方服务器处理静态页面的环境中。在这种情况下，动态内容（动态页面最好由HTTP Servlet或JSP生成）委托给BEA WebLogic Server ，它可能运行在不同的进程中或者在不同的主机上。对于最终用户——浏览器来说，将HTTP请求委派给BEA WebLogic Server 看起来仍然是来自相同的源。<BR><B>　　</B>在这种情况下，代理插件也可以访问由BEA WebLogic Server所托管的业务组件（如图2所示）。</P>
<P align=center><IMG height=358 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022806.gif" width=550><BR>图2：BEA WebLogic代理插件连接BEA WebLogic Server</P>
<P><B>　　</B>该配置使用户可将其投资利用到第三方Web服务器中，从而提供到基于WebLogic Server的环境的平稳过渡，同时支持与第三方服务器密切整合，并且支持BEA WebLogic本身的负载均衡和故障切换能力。<A id=8 name=8></A></P>
<P><B>硬件负载均衡器</B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>硬件负载均衡器克服了DNS方法的缺点，它工作在IP级而不是在命名级。同时，硬件负载均衡器比代理服务器所实现的软件均衡器的性能更好。硬件负载均衡器相当于集群的代理。客户机连接到负载均衡器上，并且将该连接路由到支持它的集群服务器中的某一服务器。负载均衡硬件可以跟踪每台服务器的“可用状态”，避免向“反常的”访问器发送请求。在其负载均衡判定中，负载均衡硬件也可以结合负载信息。使用负载均衡硬件更容易看见的一些优点是：</P>
<UL>
<LI>更宽的负载均衡算法选择范围——您可以选择各种各样的负载均衡算法。例如，您可以根据负载、连接或者简单的“循环”逻辑来选择算法。 
<LI>跳过失效的服务器——负载均衡器将跳过集群中失效的服务器，从而提高响应时间。 
<LI>加速安全套接字层（Secure Socket Layer，SSL）——负载均衡器通常将SSL处理从应用程序服务器转移到专用SSL加速器上，因而可以加速SSL。通过减少处理安全事务所需的时间，这可以极大地提高性能。 
<LI>较少的网络跳数（hop）——在BEA WebLogic Server集群前面直接使用一个硬件负载均衡器导致从负载均衡器到BEA WebLogic Server 只有一个网络跳。这样比将负载均衡器放在第三方代理Web服务器场之前性能要好，因为那样需要标准的两跳——一跳是从负载均衡器到Web服务器，另一跳是从Web服务器到BEA WebLogic Server。 </LI></UL>
<P><B>　　</B>然而，硬件负载均衡器一般比用BEA WebLogic Server充当代理更昂贵，并且没有BEA WebLogic Server特有的智能，该智能可提供更好的故障切换性能、降低网络混乱程度。特别是，硬件负载均衡器不能利用HTTP cookies，这些cookies载有BEA WebLogic集群特有的有关集群对象的主实例和副实例位置的信息。<BR><B>　　</B> BEA WebLogic Server支持大多数现在市场上流行的负载均衡硬件产品。注意一点很重要，联合使用这些负载均衡产品和BEA WebLogic Server不需要额外的编码。<BR><B>　　</B>要支持通过负载均衡硬件的直接客户机访问，BEA WebLogic Server的复制系统（replication system）使客户机能够使用副（secondary）会话状态，而不用管客户机故障切换到哪台服务器。BEA WebLogic Server继续使用客户机方的cookies或者URL重写功能，以记录主服务器和副服务器的位置。然而，在部署负载均衡硬件时，这一信息只能用作客户机会话状态位置的历史。通过负载均衡硬件访问集群时，客户机不使用cookie信息主动查找故障后的服务器位置。简而言之，负载均衡硬件使用它的配置策略将请求定向到集群中某一可用BEA WebLogic Server。</P>
<P align=center><IMG height=371 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022807.gif" width=550><BR>图3：硬件负载均衡器连接BEA WebLogic Server</P>
<P><B>　　</B>一旦服务器选定哪台客户机将进行故障切换，该服务器就使用该客户机cookie中的信息（或者如果使用URL重写功能，就使用HTTP请求中的信息）获得会话状态复制品（稍后会更详细地讨论）。故障切换过程对客户机仍保持完全透明。<A id=9 name=9></A></P>
<P><B>WAN集群</B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>BEA WebLogic集群也可以配置在广域网（WAN）上。在这种情况下，尽管配置成单一的集群，但BEA WebLogic Server实例在物理上可能位于不同的数据中心、属于不同的局域网（LAN），并且可能相距很远。这些配置通常用于灾难恢复，或者提供从地理上分布的客户机到全球分布的应用程序（不同的数据中心托管相同的应用程序）的快速本地访问。</P>
<P><B>WAN集群方案：</B><BR>1.在构成WAN集群的两个或者多个LAN之间有可靠的、高吞吐能力的连接。该连接可以是专用线路，或者其他受控的“宽管（fat pipe）”型连接。从BEA WebLogic集群的角度来看，这种情况与简单的LAN情况没什么差别。集群可以为持久性状态使用内存中（in-memory）复制，并且两个LAN之间的所有路由器必须允许多播和点到点连接的TCP/IP。</P>
<P align=center><IMG height=353 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022808.gif" width=550><BR>图4：具有可靠的、高吞吐量连接的WAN集群</P>
<P><B>　　</B>要使WAN集群高度可用，应该跨WAN生成复制组，以便主和副服务器不在同一数据中心（如图4所示）。</P>
<P>2.没有高吞吐量、可靠的连接可以利用，并且每个LAN只能通过常规的Internet连接访问集群的其他部分，并且没有受控的集线器和路由器。<BR><B>　　</B>值得推荐的是，在这种情况下，只能使用基于磁盘的持久性（基于JDBC－或者文件）。利用这种配置，子集群不能相互通信。然而，他们使用持久的状态存储设备通过Internet进行复制。使用市场上销售的文件和数据库复制产品（用于文件的Veritas，用于数据库的Oracle等）可以实现这一功能。但是，磁盘存储器复制可能不是实时的，即：它可能根据配置的时间间隔进行复制。测试表明：基于JDBC的持久性比基于文件的持久性要快得多。</P>
<P align=center><IMG height=476 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022809.gif" width=550><BR>图5：没有可靠连接的 WAN集群 </P>
<P><B>　　</B>在此方案中（如上面的图5所示），如果第1个数据中心发生故障，请求将被重路由到另外一个数据中心，对象的状态将从持久性存储器中重载，但自最后一次成功复制之后，可能有些信息没有被复制，因而可能丢失。<BR><B>　　</B>为了使BEA WebLogic WAN集群能够实现，必须使用像Alteon、F5、和Resonate这样的第三方全局负载均衡器（Global Load Balancer）（也称为Global Content Manager）。<BR>为了实现最高的可用性，WAN 集群可能需要大量的网络和系统资源。<A id=10 name=10></A></P>
<P><B>Web 集群</B> <B></B><B></B><A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>BEA WebLogic Server通过保持访问集群Servlet和Java Server Page（JSP）的客户机的HTTP会话状态，为Web应用程序提供集群支持。这种持久性可以通过配置BEA WebLogic控制台获得，可以从下面三个互斥选项中选择：</P>
<UL>
<LI>In-memory复制 
<LI>文件系统持久性 
<LI>通过JDBC的数据库持久性 </LI></UL>
<P><B>　　</B>每个选项分别表现了持久性和性能之间的一种折衷。文件或数据库的持久性提供可能的最可靠持久性。然而，这些选项就性能而言代价更大。In-memory复制通过Servlet和JSP HTTP会话状态自动故障切换为客户提供高可用性优点。该选项最适合那些为了获取最优性能而愿意接受持久性折衷方案的客户。<BR><B>　　</B>In-memory复制通过创建主会话来提供客户机会话状态的持久性。而主会话保存在客户机最初连接到的BEA WebLogic Server实例上，并且在集群内的另一个BEA WebLogic Server实例上有该会话状态的副复制品。复制品总是保持最新，以便托管主会话状态的服务器发生故障时可以使用它。从一个实例复制会话状态到另一个实例的过程正是In-memory复制的机制。<BR><B>　　</B>如果可能，BEA WebLogic Server在不同的机器上（有别于托管主会话状态的机器）创建会话状态的复制品。BEA WebLogic Server使您能够利用Administration Console中的复制组（replication group）进一步控制存放副状态的地方。复制组只是用于存储会话状态复制品的集群实例优先级列表。<BR><B>　　</B>图6描述了客户机利用BEA WebLogic代理或者硬件负载均衡器访问服务器集群。该示例使用BEA WebLogic Server集群处理静态和动态的Web内容，以及托管应用程序的业务逻辑。所有HTTP请求通过负载均衡器转发给集群。</P>
<P align=center><IMG height=509 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022810.gif" width=550><BR>图6：负载均衡（硬件或者代理）将请求发送给集群内的某一可用服务器 </P>
<P><B>　　</B>当客户机请求Servlet时，硬件负载均衡器代理到集群的请求。负载均衡器利用它的配置策略，将客户机请求发送到Server A之后，Server A充当客户机Servlet会话状态的主托管机。<BR><B>　　</B>在上面的图6中，Server A使用特殊的算法托管会话状态的复制品。选择Server B托管该复制品。通知客户机在本地cookie中记录Server A和Server B的位置。如果客户机不使用cookies，主服务器和副服务器的记录可以记录在通过URL重写功能返回客户机的URL中。<BR><B>　　</B>当客户机向集群提出另外的请求时，负载均衡器使用客户机方cookie中的标识符来确保这些请求继续发送到Server A（而不是被负载均衡到集群内的其他服务器）。这就确保在会话生存期内，客户机总是保持与托管主会话对象的服务器相关联。<BR><B>　　</B>文件系统和数据库持久性工作方式类似，只是状态数据保存在磁盘上。在这种情况下，BEA WebLogic Server实例之间没有集群感知的占位程序或者状态复制。在故障切换处理期间，保存的状态由下一个从负载均衡器中得到请求的可用服务器实例从磁盘上读取。<A id=11 name=11></A><BR><BR><B>用代理Web服务器的故障切换</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>在客户机会话期间，万一Server A出现故障，客户机到Server A的下一个连接请求也会失败。<BR><B>　　</B>发生这种情况时，代理可以确定（从cookie或者URL）JSP或者Servlet对象要访问的副服务器（在该例中是Server B）。请求被代理直接重路由到副服务器（B）。之后，Server B指定自己作为主服务器，并在集群内的任何其他可用服务器上创建另一个副服务器（C）（如图7所示）。</P>
<P align=center><IMG height=468 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022811.gif" width=550><BR>图7：具有代理负载均衡的主服务器出现故障，则将请求重定向到集群中的副服务器<A id=12 name=12></A></P>
<P><B>用负载均衡硬件的故障切换</B><B></B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>负载均衡硬件为了响应连接失败，会使用其配置策略把请求发送给集群中某一可用的BEA WebLogic Server。在上述示例中，假设在Server A出现故障后，负载均衡器会将客户机的请求定向到Server C。<BR><B>　　</B>当客户机连接到Server C时，服务器使用客户机cookie中的信息(或者如果使用了URL重写功能，则使用HTTP请求中的信息)获取Server B上的会话状态复制品。故障切换的过程足够快，因而系统故障对客户机是完全透明的。<BR><B>　　</B>Server C变成客户机主会话状态的新托管主机，Server B继续托管会话状态的复制品。在客户机cookie中，或者通过URL重写功能，会再一次更新有关主、副托管主机的新信息。</P>
<P align=center><IMG height=469 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022812.gif" width=549><BR>图8：具有负载均衡硬件的主服务器出现故障，它将请求重定向到集群中的某一可用服务器</P>
<P><B>　　</B>在两个服务器集群中，客户机将透明地故障切换到托管副会话状态的服务器。另一台服务器一加入到该集群中，或者原来的主服务器重新启动或重新连接到网络中，就会继续客户机会话状态的复制。<A id=13 name=13></A></P>
<P><B>对象集群</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>BEA WebLogic Server支持的第二种形式的集群是对象集群。如果某一对象被集群，该对象的实例——称为复制品——将被部署在集群内的所有服务器上。<BR><B>　　</B>在BEA WebLogic Server中，支持集群对象的关键技术是复制品感知的占位程序。连接到BEA WebLogic Server集群、并且查找集群对象的客户机获得一个该对象的占位程序，在调用者看来，它是一般的占位程序。但是，该占位程序不是表示单一的对象，而是表示一个复制品集合。<BR><B>　　</B>通过使用复制品感知的占位程序和全局共享和复制的JNDI树，客户机可以选择调用对象的哪一个实例。<BR><B>　　</B>默认情况下，在部署对象、然后连入JNDI树的时候，会自动生成该对象的复制品感知的占位程序。如果该对象不需要复制，可以关闭这一特性。复制品感知的占位程序含有查找某一对象所需的逻辑，而这一对象在部署该对象的任意BEA WebLogic Server实例上。<BR><B>　　</B>BEA WebLogic Server实例有能力更新该占位程序的本地副本，因而该占位程序含有托管对象复制品的所有可用服务器实例的完整列表。该占位程序还包含在其主服务器之中分配负载时的负载均衡逻辑。每次调用时，占位程序利用其负载均衡算法选择调用哪一个复制品。这种在集群中负载均衡对调用者是透明的。如果在调用过程中出现故障，占位程序截获异常并重新尝试调用另一个复制品。这种故障切换对调用者也是透明的。<BR><B>　　</B>万一出现故障，占位程序从其列表中删除失效的服务器实例。如果在其列表中没有服务器了，则它再次使用DNS查找某个正在运行的服务器，获得当前正在运行的实例列表。此外，占位程序定期地刷新集群内可用的服务器实例列表。这使得占位程序能够利用添加到集群中的新服务器。<BR><B>　　</B>BEA WebLogic Server中的对象集群根据维护实体bean和会话bean状态的重要性，有差别地处理实体bean和会话bean。下面描述对各种EJB（Enterprise JavaBean）的不同处理方法。<A id=14 name=14></A></P>
<P><B>无状态会话EJB</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>因为无状态bean不保存代表客户机的状态，所以占位程序自由地将所有调用路由到托管该bean的任何服务器。因为bean是集群的，所以在故障事件中，占位程序可以自动进行故障切换。<A id=15 name=15></A></P>
<P><B>有状态会话EJB</B><B></B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>BEA WebLogic Server复制有状态会话bean EJB的状态，方法与它复制HTTP会话状态的方法类似。当客户机创建EJBObject占位程序时，最初由负载均衡器选择的实例会自动选择托管已复制EJB状态的副服务器。<BR><B>　　</B>客户机接收一个复制品感知的占位程序，它列出集群内托管EJB状态的主、副服务器的位置。主服务器托管与客户机交互的实际EJB实例。副服务器只托管复制的EJB状态，这只消耗很小的内存空间。除非发生故障切换，否则副服务器不创建实际的EJB实例。这样保证在副服务器上消耗的资源最少，保存复制的EJB状态不需要配置额外的EJB资源。<BR><B>　　</B>当客户机修改EJB的状态时，状态变化被复制到副服务器实例上。对于使用事务的EJB，事务提交后，复制立即发生。对于不使用事务的EJB，调用每个方法后复制发生。在这两种情况下，只有EJB状态的实际变化被复制到副服务器上，从而保证与复制过程相关的开销最小。 <BR><B>　　</B>如果主服务器出现故障，客户机的EJB占位程序会将以后的请求重定向到副BEA WebLogic Server实例。此时，副服务器利用复制的状态数据创建一个新的EJB实例，并在副服务器上继续处理。<BR><B>　　</B>故障切换后，BEA WebLogic Server选择一个新的副服务器来复制EJB会话状态（如果在集群中有其他可用服务器的话）。在下一个方法调用中，将会自动更新客户机的复制品感知占位程序中的新的主、副服务器实例位置。<A id=16 name=16></A></P>
<P><B>实体bean</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>当EJBHome找到或者创建一个实体bean之后，它返回订住该服务器的占位程序。该服务器出现故障之前，有关该占位程序的所有请求都发往相同服务器。在这种情况下，BEA WebLogic Server故障切换至另一台服务器。注意：在这种情况下没有复制，BEA WebLogic Server依靠数据库来存储变化情况。由于EJB部署在集群内，所以只需要另一台可用的服务器从数据库中重载其状态。此时，占位程序将会用托管重新创建的EJB的服务器位置进行更新。<BR><B>　　</B>实体bean的负载均衡只在主（home）级进行。然而，为了故障切换，复制品感知占位程序将在另一台服务器上重试请求。应该注意的是：只有请求在当前事务外时，才是这种情况。 <BR><B>　　</B>由于在集群内可能存在该实体bean的多个实例，所以每个实例都必须在每个事务之前从数据库中读取，并且每次提交时写入。<BR><B>　　</B>如果是只读的实体bean，bean的数据由访问该bean的服务器实例在本地缓存。在数据很少修改的情况下，这样做节省数据库访问。当这些数据是由只读的EJB修改时，通过向服务器的其他实例多播无效消息，可明确地使集群内相关的只读bean失效。这种失效方法可用于指定的主键、某一键集或者所有指定类型的bean。<A id=17 name=17></A></P>
<P><B>全部集群的JNDI树</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>某一单独BEA WebLogic Server的客户机使用JNDI兼容的命名服务访问对象和服务。JNDI命名服务包含一系列由服务器提供的公共服务，以“树形”结构进行组织。BEA WebLogic Server通过将名称绑定到表示新服务的JNDI树来提供新的服务。客户机通过连接到服务器并查找服务的绑定名称来获得服务。<BR><B>　　</B>集群内的服务器实例利用全部集群的JNDI树。全部集群的JNDI树与单一服务器的JNDI树类似，在这种情况下该树包含一系列可用的服务。但是，全部集群的JNDI树除了保存本地服务的名称之外，它还保存复制品感知的占位程序，而该占位程序指向集群内所有服务器上可用的集群对象。集群内的各BEA WebLogic Server实例创建和维护全部集群JNDI树的一个本地副本。<A id=18 name=18></A></P>
<P><B>JMS 集群</B><B></B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>BEA WebLogic Server通过在整个集群内分布JMS目的地和连接工厂来支持多个JMS服务器的集群。<BR><B>　　</B>JMS目的地是接收消息的实体。客户机使用JMS连接工厂建立与目的地的连接。<BR><B>　　</B>利用集群，BEA WebLogic Server的高级JMS可伸缩性可通过如下方法实现：<BR><B>　　</B>通过连接工厂在多个JMS服务器中分配应用程序的负载。这样提供多个服务器间的连接负载平衡，减少单台JMS服务器上的负载，并通过将连接路由到指定服务器而使会话集中。<BR><B>　　</B>通过虚拟目的地标识跨多个JMS服务器分配目的地。这可减少指定服务器上各物理目的地的负载，并且极大地提高消息传递的可靠性和性能。<BR><B>　　</B>可选的多播支持可以减少JMS服务器必需传送的消息数目。JMS服务器只将消息的单个副本转发给每个与多播IP地址相关的主机组，而与订阅的应用程序数目无关。<BR><B>　　</B>JMS连接工厂可以自动部署在集群内的多台服务器上。使用分布式JMS连接工厂，客户机可以从BEA WebLogic Server的任意实例中取得一个JMS连接。每台BEA WebLogic Server处理一组JMS目的地请求。<BR><B>　　</B>当JMS客户机请求来自BEA WebLogic Server集群的JMS连接时，它可能根据所用的负载均衡算法从集群的任意服务器上获得。该服务器可能不是处理客户机所需的到指定目的地JMS请求的服务器。但使用JNDI，部署在每个BEA WebLogic Server实例上的连接工厂可以访问集群内任意服务器的目的地组。这样每台客户机可以在集群范围内透明地访问集群内的任何目的地。<BR><B>　　</B>使用虚拟目的地，允许您配置多个物理目的地作为单一分布式目的地集合的成员，这样万一某台服务器出现故障，BEA WebLogic JMS的高可用性实现可提供某种程度的服务连续性。特别是，管理员可以配置集群内某一给定目的地的多个实例作为虚拟目的地的成员。如果集群内的一个实例出现故障，则同一目的地的其他实例能够为JMS过程和消息使用者提供服务。在这种情况下，可以将遗留在故障服务器上的可迁移目的地服务消息迁移到正在运行的实例上。<BR><B>　　</B>虚拟目的地和遍及整个集群的分布式连接工厂，使管理员能够通过配置不同的负载均衡算法和为集群各节点设置相对权重来人工均衡JMS服务的负载。<A id=19 name=19></A></P>
<P><B>JDBC元池集群</B><B></B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>万一正在服务的集群成员发生故障，在不更改连接参数的情况下，集群JDBC（元池）允许外部的JDBC客户机自动重新连接和重启它们的JDBC连接。集群JDBC需要DataSource对象和BEA WebLogic RMI驱动程序以连接到DBMS。利用Administration Console 可以为每个BEA WebLogic Server定义DataSource对象。JDBC元池为部署在BEA WebLogic Server集群内多台服务器上的JDBC连接池提供集群。当客户机向元池请求连接时，集群选择将提供连接的服务器，进行负载均衡和保护，以防止服务器失效。一旦客户机拥有一个连接，由JDBC驱动程序保存的状态使它必需将客户机与主BEA WebLogic Server绑定在一起。<A id=20 name=20></A></P>
<P><B>BEA WebLogic Server实例如何在网络上通信</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>每个BEA WebLogic Server实例运行在惟一IP地址的网络上。整个集群的监听端口必须相同。BEA WebLogic Server允许多宿主（multi-homing），这意味着可以分配多个IP地址给同一台物理机器。因此多宿主主机有多个网络接口，每个网络接口可以运行一个BEA WebLogic Server实例。集群内BEA WebLogic Server实例彼此利用IP多播通信，用以向集群内的所有服务器复制某类信息。IP多播是一种简单的广播技术，它使多个应用程序能够向某一给定IP多播地址和端口号“订阅”，并监听消息。<BR><B>　　</B>集群中的每台服务器都配置常用的多播地址。当服务器向集群的多播地址发送消息时，所有的服务器都接收消息。这比让服务器发送点到点消息的效率要高得多，但它的确要求集群中的所有服务器必须在支持多播的网络上。多播不能通过Internet上进行，因而集群不能经过Internet。<BR><B>　　</B>BEA WebLogic Server把IP多播用于集群中各个服务器实例的所有一对多通信。例如：</P>
<UL>
<LI>集群范围内的JNDI更新——所有集群成员使用多播通知本地部署或者删除服务的可用性。服务器监视这些通知，以便它们能更新其本地JNDI树以反映当前的部署情况。 
<LI>集群“心跳（heartbeats）”——BEA WebLogic Server使用多播来广播规律的“心跳”消息，通告集群内单个服务器实例的可用性。集群内的所有服务器将监听心跳消息作为确定服务器失效时间的方法。“心跳”的使用是BEA WebLogic确定给定服务器实例是否可用的两种机制之一。BEA WebLogic还监视IP套节字错误作为确定服务器失效时更直接的方法。例如，如果服务器实例A具有到实例B的开放套节字，而该套节字突然关闭了，则Server A假定Server B脱线了。<A id=21 name=21></A> </LI></UL>
<P><B>配置和管理BEA WebLogic 集群</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>作为一个单位来管理的内部相关BEA WebLogic Server资源集称为域（domain）。一个域包括一台或者多台BEA WebLogic Server，也可能包括BEA WebLogic Server集群。域，以及集群可以跨多个物理机器生成。<BR><B>　　</B>通过Administration Console，可以使用BEA的图形用户接口（GUI）管理和监视域、集群或者单独的BEA WebLogic Server的配置。<BR><B>　　</B>BEA WebLogic集群的重要特性之一是能够把单一的视图应用于集群内配置的所有单独服务器上。在该意义上来说，集群被作为某一域内的实体进行管理。在域中配置集群时，集群的所有服务器必须也是该域的一部分。一个域可以包含多个集群。<BR><B>　　</B>正在运行管理服务的BEA WebLogic Server称为管理服务器（administration server）。管理服务提供配置和监控整个域的控制中心点。为了在该域进行任意管理操作，必须运行管理服务器。<BR><B>　　</B>在拥有多台BEA WebLogic Server的域中，只有一台服务器是管理服务器，其他服务器称为受管服务器（Managed Server）。每个BEA WebLogic受管服务器在启动时从管理服务器获得其配置。<BR><B>　　</B>在生产系统的典型配置中，关于您的业务逻辑的应用程序和组件可以部署在受管服务器上，而管理服务器的作用是配置和监视受管服务器。如果管理服务器出现故障，部署在受管服务器上的应用程序不会受影响，继续处理客户机的请求。在这样的情况下，管理服务器一旦重新启动，就可以重新获得活动域的管理控制。</P>
<P align=center><IMG height=392 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022813.gif" width=550><BR>图9：一般的BEA WebLogic Server域架构，它含有单独的服务器、集群、多台物理机器和单台管理服务器<A id=22 name=22></A></P>
<P><B>配置集群</B><B></B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><BR><B>　　</B>利用BEA WebLogic Server的Domain Configuration Wizard，可以简单快速地配置集群。通过直观的图形接口，管理员可以通过检查一系列的窗口并输入特定的参数来创建集群域。这些参数配置拥有管理服务器、受管服务器、物理机器，以及特定应用程序的JDBC数据源参数、JMS工厂和目的地、安全实体、JVM以及其他基本子系统和组件（图10）的集群拓扑。验证并记录用户提供的信息，并由这些信息生成完整的集群配置。</P>
<P align=center><IMG height=429 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022814.gif" width=550><BR>图10：用Domain Configuration Wizard配置BEA WebLogic Cluster</P>
<P><B>　　</B>Domain Configuration Wizard也可以创建域模板，这样可以复制复杂的环境，简化将集群应用程序转入生产的冗长过程。<BR><B>　　</B>BEA WebLogic Server域和集群的持久性配置保存在一个XML配置文件中。管理服务器动态管理该配置，并且用户可以通过Administration Console使用它（图11）。</P>
<P align=center><IMG height=544 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022815.gif" width=519><BR>图11：通过BEA WebLogic Server Administration Console管理集群域</P>
<P><B>　　</B>在Administration Console中可以完成的集群配置任务包括：</P>
<UL>
<LI>利用Administration Console的Cluster节点配置服务器集群。利用该节点可以修改的属性包括Cluster Name、Cluster ListenPort和集群中的服务器名称。 
<LI>利用Administration Console的Cluster节点复制服务器集群。集群被复制，保持属性值和原集群中各个服务器，以及在Server节点的Configuration部分设置的新集群名称。 
<LI>利用Administration Console的Cluster节点监视集群中的服务器。 
<LI>利用Administration Console的Cluster节点为集群指派服务器。 
<LI>利用Administration Console的Cluster节点删除集群。<A id=23 name=23></A> </LI></UL>
<P><B>使用节点管理器（Node Manager）自动管理</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>BEA WebLogic Server生产环境中的受管服务器通常被分布在多台机器和多个地理位置。自动管理允许集群自管理和自动地对各种突发条件和情况做出反应。利用节点管理器实用程序大大方便了这一强大的特性。<BR><B>　　</B>节点管理器是一个Java实用程序，在BEA WebLogic Server（图12）中作为独立的进程运行，允许您执行受管服务器的常规操作任务，而不管它与其管理服务器的相对位置如何。虽然节点管理器的使用是可以选择的，但如果您的BEA WebLogic Server环境以高可用性要求托管应用程序，则它可提供极有价值的优点。</P>
<P align=center><IMG height=366 src="http://dev2dev.bea.com.cn/techdoc/webser/image2005022816.gif" width=200><BR>图12：多机集群域中的节点管理器</P>
<P><B>　　</B>与只刷新用户请求信息显示的Administration Console不同，节点管理器连续地监视受管服务器。通过基于JMX的开放管理接口，客户可以基于节点管理器构建强大的事件通知和统计监视框架。同时，可以配置节点管理器自动对集群事件做出反应，并根据预先配置的条件采取某些行动。例如，节点管理器可以在突然失效后自动重启某台受管服务器，或者如果由某台服务器所报告的某一统计参数低于配置的阈值时，则给管理员发送消息。</P>
<P><B>　　</B>有关配置和管理BEA WebLogic服务器、集群和域的更多信息，请参阅BEA WebLogic在线文档，其网址是：<A href="http://e-docs.bea.com/wls/docs81/adminguide/" target=_blank>http://e-docs.bea.com/wls/docs81/adminguide/</A>。<A id=24 name=24></A></P>
<P><B>结束语</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>本白皮书概述了BEA WebLogic Server中的集群特点，说明了BEA WebLogic架构如何使用集群技术提供企业客户所需的高可用性和可伸缩性。<BR><B>　　</B>有关BEA WebLogic集群技术的更多细节，请参阅BEA WebLogic Server文档，其网址是：<A href="http://e-docs.bea.com/wls/docs81" target=_blank>http://e-docs.bea.com/wls/docs81</A>。<BR><B>　　</B>BEA WebLogic集群的目的之一是消除集群内的单点故障。这一点是通过架构实现冗余来达到的。在硬件或者电源故障事件中，客户机能够利用这种冗余自动故障切换到某一正在运行的系统资源上。正是这种避免停机的能力为BEA客户提供了24×7可管理的保证。<BR><B>　　</B>另一个目标是推动高度可伸缩的企业电子商务基础结构，这在当今的动态业务环境中可使增长能力无限。<BR><B>　　</B>BEA相信BEA WebLogic集群技术是业内最先进的，并且向客户提供最高级别的可伸缩性和任务关键型应用程序最需要的可靠性。<BR><B>　　</B>BEA WebLogic集群，加上其他的性能和可伸缩性特性，例如JDBC连接池和多池、高级缓存、只读/可改写的只读实体bean以及线程池，使WebLogic Server成为市场上最可靠、最具伸缩性和性能最强的Java应用服务器。这也是一些大客户，如FedEx、Charles Schwab、Amazon.com和很多其他客户在WebLogic Server上构建其任务关键型应用程序的原因。<BR><B>　　</B>BEA WebLogic Server是BEA E-Business Platform?的基础。BEA E-Business Platform专门设计成能够跨多个应用程序和系统共享数据和服务，它是构建在各种开放标准之上的基础结构产品集成套件，这些开发的标准支持大量事务、业务流程管理、应用程序集成、企业内和企业之间的业务协作以及创建和维护动态电子市场（e-market）的能力。<BR><B>　　</B>要了解关于BEA WebLogic Enterprise Platform?的更多信息，请访问 BEA Web站点，其网址是： <A href="http://www.bea.com/" target=_blank>www.bea.com/</A>。<A id=25 name=25></A></P>
<P><B>BEA 简介</B><B></B> <A href="http://dev2dev.bea.com.cn/techdoc/webser/2005030102.html#0">TOP</A><B><BR>　　</B>BEA是世界上领先的应用程序基础架构软件公司，在世界各地的客户超过13000家，包括Fortune全球500强企业中的大多数。BEA WebLogic Enterprise Platform提供工业强度（industrial strength）的、易于使用的软件基础，使企业更灵活、生产率更高和连接更强，从而显著地增加了IT生产率并加快了投资回报。BEA的平台是超过1700家系统集成商、独立软件开发商，以及与BEA合作以确保成功部署客户解决方案的应用程序服务提供商们的事实标准。</P>
<P>BEA Systems, Inc.<BR>2315 North First Street<BR>San Jose, CA 95131 U.S.A.<BR>电话: +1.408.570.8000<BR>传真: +1.408.570.8901<BR>网址：<A href="http://www.bea.com/" target=_blank>http://www.bea.com</A> </P>
<P>原文出处：<BR><A href="http://dev2dev.bea.com/products/wlserver81/whitepapers/WLS_81_Clustering.jsp" target=_blank>http://dev2dev.bea.com/products/wlserver81/whitepapers/WLS_81_Clustering.jsp</A></P><img src ="http://www.blogjava.net/kapok/aggbug/7423.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-07-09 20:52 <a href="http://www.blogjava.net/kapok/archive/2005/07/09/7423.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>俺没钱，拿来当课程表自学得了</title><link>http://www.blogjava.net/kapok/archive/2005/07/09/7395.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 08 Jul 2005 17:09:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/07/09/7395.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/7395.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/07/09/7395.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/7395.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/7395.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=600 align=center border=0>
<TBODY>
<TR>
<TD vAlign=bottom align=middle background=http://www.bea.com.cn/edm/images/image05032204.jpg height=89>
<TABLE cellSpacing=0 cellPadding=0 width=544 border=0>
<TBODY>
<TR>
<TD class=text vAlign=top align=middle>
<TABLE class=text cellSpacing=0 cellPadding=0 width=515 border=0>
<TBODY>
<TR>
<TD width=515>尊敬的BEA 用户： <BR>　　您好！</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD align=middle background=http://www.bea.com.cn/edm/images/image05032205.gif>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD>　　<SPAN class=style1><FONT color=#ff0000>SOA 浪潮</FONT></SPAN>正以无可阻挡的势头前进，你是否已准备好迎接新技术的挑战，为自己的未来做好规划与奋斗？现在BEA公司培训部提供这个<SPAN class=style1><FONT color=#ff0000>绝好的机会</FONT></SPAN>来帮助你武装自己，使你能够站在科技的浪尖迎接辉煌。<BR>　　 BEA 公司作为世界领先的SOA 倡导者和实践者，<SPAN class=style1><FONT color=#ff0000>为广大用户和技术人员定制了实现SOA目标的培训课程</FONT></SPAN>，帮助广大的用户和合作伙伴奠定SOA实施的坚实基础！</TD></TR>
<TR>
<TD>&nbsp;</TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=544 border=0>
<TBODY>
<TR>
<TD class=text vAlign=bottom bgColor=#f7f7f7 height=23><B><FONT color=#000000>&nbsp;&nbsp;&nbsp;优惠课程介绍</FONT></B></TD></TR>
<TR>
<TD class=text background=http://www.bea.com.cn/edm/images/line1.gif height=1><IMG height=1 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD>&nbsp;</TD></TR>
<TR>
<TD>
<TABLE class=text2 cellSpacing=1 cellPadding=3 width="100%" border=0>
<TBODY>
<TR>
<TD><SPAN class=style2><STRONG><FONT color=#990000>SOA + WebService 开发</FONT></STRONG></SPAN></TD>
<TD vAlign=center align=middle width=80><A href="http://www.bea.com.cn/edm/index050530.html#a1">课程介绍</A><BR></TD>
<TD vAlign=center align=middle width=80><A href="http://www.bea.com.cn/edm/index050530.html#a2">课程内容</A></TD>
<TD vAlign=center align=middle width=80><A href="http://www.bea.com.cn/edm/index050530.html#a3">报名条件</A></TD>
<TD vAlign=center align=middle width=50><A href="http://www.bea.com.cn/edm/index050530.html#a4">师资</A></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD>&nbsp;</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text vAlign=top height=1><B><FONT color=#000000>&nbsp;&nbsp;&nbsp;联系方式<IMG height=24 src="http://www.bea.com.cn/edm/images/tc.gif" width=59 align=absBottom></FONT></B></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD height=9><IMG height=9 src="http://www.bea.com.cn/edm/images/td.gif" width=540></TD></TR>
<TR>
<TD align=middle background=http://www.bea.com.cn/edm/images/te.gif>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD width=416><B><FONT color=#000000>关于这次的培训课程，欢迎致电BEA：</FONT></B><FONT color=#000000><BR>免费销售热线：<FONT color=#ff0000><B>800-810-8878</B></FONT>或拨直线电话：010-85281188<BR>电 子 邮 件： <A href="mailto:education.cn@bea.com">education.cn@bea.com</A></FONT></TD>
<TD vAlign=center align=right width=98><IMG height=68 src="http://www.bea.com.cn/edm/images/tg.gif" width=76></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD><IMG height=9 src="http://www.bea.com.cn/edm/images/tf.gif" width=540></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD><A name=a4></A></TD></TR>
<TR>
<TD vAlign=bottom><B><FONT color=#000000>师资力量<IMG height=41 src="http://www.bea.com.cn/edm/images/ta.gif" width=42 align=absBottom></FONT></B> </TD></TR>
<TR>
<TD class=text background=http://www.bea.com.cn/edm/images/line1.gif height=1><IMG height=1 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD>　　 培训直接由富有项目经验的培训教师授课，BEA公司将免费为您提供培训教材及培训期间的午餐。完成该课程的学习，还可获得由毕益辉系统（中国）有限公司颁发的培训结业证书。 <BR>　　 我们将竭诚为您提供良好的培训环境，包括：一流的培训教室，人手一台高配置的台式电脑。经验丰富的BEA高级技术讲师将配合实际案例和学员实行互动式教学，用丰富的授课方式为您提供灵活、个性化和方便快捷的培训服务。</TD></TR>
<TR>
<TD><A name=a3></A></TD></TR>
<TR>
<TD vAlign=bottom><B><FONT color=#000000>报名条件</FONT></B></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text background=http://www.bea.com.cn/edm/images/line1.gif height=1><IMG height=1 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD><B></B></TD></TR>
<TR>
<TD>
<UL>
<LI>有意参加培训的学员需于开课前至少一周报名和支付全额培训费用； 
<LI>由于本次培训只举行一次，学员不能变更参加培训的时间；如因任何原因，未能参加已报名的培训，将自动丧失培训资格；所付之款项不予退还。 
<LI>如报名人数未达最低人数，BEA有权取消本次培训；BEA将及时通知已报名学员并安排退回已付款项。 </LI></UL></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text background=http://www.bea.com.cn/edm/images/line1.gif height=1><IMG height=1 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text height=8><IMG height=8 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>&nbsp;</TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD vAlign=bottom><B><FONT color=#000000>优惠课程介绍</FONT></B></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text background=http://www.bea.com.cn/edm/images/line1.gif height=1><IMG height=1 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD><SPAN class=style1><A id=a1 name=a1></A></SPAN></TD></TR>
<TR>
<TD><SPAN class=style1><FONT color=#ff0000>课程名称：SOA + WebService 开发<BR>课程代码：PLAT-R51-D29-81<BR>天数： 2 天 SOA + 3 天 WebService（共计5天）<BR>授课时间：7月11日—7月15日<BR>课程价格：￥6000元 1人 或 ￥10000元 2人 </FONT></SPAN>
<P><STRONG>课程介绍:</STRONG><BR>　　这个课程提供详细的基于BEA WebLogic Platform 8.1的面向服务架构的技术培训。同时针对SOA进行深入的分析和BEA WebLogic Platform 8.1如何支持SOA架构。你将可以设计和开发一个层次化架构模式的SOA和在BEA WebLogic Platform 8.1上部署你的解决方案。<BR>　　 课程同时介绍如何创建企业级WEB服务和在BEA WebLogic Server 8.1部署WEB服务。这个培训讲授异步WEB服务的创建、测试、查错和部署。你将学习介绍面向服务架构和支持技术，如SOAP、WSDL和UDDI的概念。 <BR>　　通过学习这个培训课程你会学习在BEA WebLogic Platform 8.1上如何成功开发和部署企业应用。通过培训可以和具有丰富经验的BEA讲师交流，学习产品最佳实践和资源； 能够更有效的计划和实施BEA产品的项目<FONT color=#ff0000><A id=a2 name=a2></A></FONT><BR><BR><STRONG>课程内容</STRONG> </P>
<TABLE class=text cellSpacing=1 cellPadding=4 bgColor=#000000>
<TBODY>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">
<P><STRONG>课程模块</STRONG><STRONG></STRONG></P></TD>
<TD vAlign=top width="80%">
<P><STRONG>主要内容</STRONG><STRONG></STRONG></P></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">
<P>SOA介绍</P></TD>
<TD vAlign=top width="80%">
<UL>
<LI>企业应用程序开发面临的挑战 
<LI>SOA的本质 
<LI>SOA的基本概念 
<LI>SOA相关技术标准 </LI></UL>
<P>实验:</P>
<UL>
<LI>建立BEA ED.Lab试验环境 
<LI>建立开发环境 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">
<P>构建连接层</P></TD>
<TD vAlign=top width="80%">
<UL>
<LI>连接层在SOA实现中扮演的角色 
<LI>JCA及JDBC在连接层的角色 
<LI>企业应用程序服务化 
<LI>建立视图控件 
<LI>建立数据控件 
<LI>建立消息控件 </LI></UL>
<P>实验:</P>
<UL>
<LI>使现存的数据库支持服务 
<LI>使企业应用支持服务 
<LI>使基于消息机制的应用系统支持服务 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">构建编排层</TD>
<TD vAlign=top width="80%">
<UL>
<LI>组成编排层的组件 
<LI>组合服务、业务服务、数据服务 
<LI>如何暴露服务 
<LI>构建Java 控件 
<LI>建立Java 流程定义 </LI></UL>
<P>实验:</P>
<UL>
<LI>建立组合式服务 
<LI>建立数据服务 
<LI>建立业务服务 
<LI>把一个服务作为 Web Services暴露 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">构建复合应用层</TD>
<TD vAlign=top width="80%">
<UL>
<LI>建立服务的前端程序 
<LI>在 Portal中提供服务 
<LI>建立Remote Portlets 
<LI>使用 Remote Portlets </LI></UL>
<P>实验:</P>
<UL>
<LI>使用 Remote Portlets </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">管理服务</TD>
<TD vAlign=top width="80%">
<UL>
<LI>Web Services 管理规范 
<LI>Services Fabric的角色 
<LI>Service Directories 
<LI>Policies的角色 
<LI>注册及监控服务 </LI></UL>
<P>实验</P>
<UL>
<LI>注册及监控服务 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">SOAP设计模式及最佳实践</TD>
<TD vAlign=top width="80%">
<UL>
<LI>SOA架构中常用的设计模式 
<LI>同步模式 
<LI>复合模式 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">Web Services介绍&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>Web services的基本理论及核心概念 
<LI>推动Web services发展的商业及技术需求 
<LI>SOAP、WSDL及UDDI规范 </LI></UL>
<P>实验</P>
<UL>
<LI>安装BEA WebLogic平台 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">BEA WebLogic Server Web Services&nbsp;&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>BEA WebLogic Server的Web services特性及规范 
<LI>BEA WebLogic Server Web services的封装、部署及测试 </LI></UL>
<P>实验</P>
<UL>
<LI>描述 Web Service 
<LI>封装及部署Web Service </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">Web Services安全性&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>BEA WebLogic Server 安全性 
<LI>Web service 安全的基本概念 
<LI>保护 Web service的各种组件 
<LI>使用SSL保护Web service 。 
<LI>Web services相关的安全标准 </LI></UL>
<P>实验</P>
<UL>
<LI>保护 Web Service </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">使用消息驱动Bean（MDB）实现Web Services的异步性&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>定义web services的异步操作 
<LI>在 BEA WebLogic Server中配置 JMS 
<LI>使用MDB建立异步操作 
<LI>配置Web services的异步性 </LI></UL>
<P>实验</P>
<UL>
<LI>使用MDB实现web services的异步性 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">实现动态客户&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>动态调用的类型以及JAX-RPC 
<LI>使用动态代理调用web services 
<LI>使用动态调用接口调用web service 
<LI>如何在有(没有)WSDL文件的情况下调用web service </LI></UL>
<P>实验</P>
<UL>
<LI>实现动态客户 </LI></UL></TD></TR>
<TR bgColor=#ffffff>
<TD vAlign=top width="20%">使用UDDI发布及查找web services&nbsp;</TD>
<TD vAlign=top width="80%">
<UL>
<LI>使用UDDI的原因 
<LI>UDDI的broker架构 
<LI>建立 UDDI 的查找及发布请求 
<LI>使用 BEA WebLogic Server UDDI 浏览器 </LI></UL>
<P>实验</P>
<UL>
<LI>发布及查找web services </LI></UL></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text height=8><IMG height=8 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR>
<TR>
<TD class=text vAlign=top align=middle height=1>
<TABLE class=text cellSpacing=0 cellPadding=0 width=514 border=0>
<TBODY>
<TR>
<TD>
<P><FONT color=#000000>感谢并致礼！</FONT></P>
<P align=right><FONT color=#000000>BEA系统（中国）有限公司</FONT></P></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD class=text height=8><IMG height=8 src="http://www.bea.com.cn/edm/images/blank.gif" width=1></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/7395.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-07-09 01:09 <a href="http://www.blogjava.net/kapok/archive/2005/07/09/7395.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JBuilder7中调试WebLogic7.0的EJB </title><link>http://www.blogjava.net/kapok/archive/2005/06/15/6162.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 15 Jun 2005 07:51:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/15/6162.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/6162.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/15/6162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/6162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/6162.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/webser/20030472.html">http://dev2dev.bea.com.cn/techdoc/webser/20030472.html</A><A href="http://dev2dev.bea.com.cn/techdoc/webser/20030472.html"><BR><BR>http://dev2dev.bea.com.cn/techdoc/webser/20030472.html</A><img src ="http://www.blogjava.net/kapok/aggbug/6162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-15 15:51 <a href="http://www.blogjava.net/kapok/archive/2005/06/15/6162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Weblogic 实用命令行</title><link>http://www.blogjava.net/kapok/archive/2005/06/15/6161.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 15 Jun 2005 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/15/6161.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/6161.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/15/6161.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/6161.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/6161.html</trackback:ping><description><![CDATA[<BLOCKQUOTE>
<P>Enterprise JavaBean (EJB) 是一些應用程式元件，它們實現了 EJB 體系結構規範，並且是 Java 2 Enterprise Edition (J2EE) 平臺的一部分。EJB 是開發和部署分散式的、可伸縮的、交易型的、安全的、可移植的、基於元件的商業應用的理想選擇。</P>
<P><FONT color=#0099cc>Enterprise JavaBean (EJB)是一些應用程式元件，它們實現了EJB體系結構規範，並且是Java 2 Enterprise Edition (J2EE) 平臺的一部分。EJB是開發和部署分散式的、可伸縮的、交易型的、安全的、可移植的、基於元件的商業應用的理想選擇。</FONT></P>
<P>基於EJB的商業應用需要一個EJB容器，用於運行時的執行。所有遵從J2EE規範的應用伺服器，包括WebLogic 8.1在內，都提供了EJB容器。</P>
<P>EJB體系結構背後的主要動機是關係的分離：它將與應用程式基礎設施相關的部分（例如交易處理和安全性）與核心應用程式部分（例如業務邏輯）分離開來。簡言之，EJB體系結構通過指定EJB容器與EJB開發者之間職責的區別，從而達到這種關係上的分離。例如，透明地實現交易處理是EJB容器的職責，而實現業務邏輯則應由EJB開發者負責。雖然EJB容器執行任務時可能需要某些線索（hint），但是，比起實際地實現這些與基礎設施相關的活動來，提供這類線索（通過基於XML的部署描述符）的代價是非常少的。總之，這種關係分離的規則使得商業應用的開發比其他方式要高效得多。 </P>
<P>EJB體系結構規範有4種版本：1.0、1.1、2.0和2.1。事實上，EJB version 1.0已經過時了，而最新版本，即2.1，由於剛剛才出現，所以還沒有廣泛提供。BEA WebLogic Server 8.1同時支援1.1和2.0這兩種版本。我們強烈建議在WebLogic Server 8.1中進行開發時使用EJB version 2.0。 </P>
<P>實體bean是一種EJB。除了EJB背後的一般動機外，使用實體bean的特定動機是為持久儲存中的業務域（business-domain）實體提供一種駐留在記憶體中的、可共用的、物件導向的視圖。通常，業務域物件就是關聯資料庫中一個表裏面的一行。在本文中，我們討論了關於在WebLogic Server 8.1環境下Entity EJB的設計、開發和部署的特定問題。至於關於EJB技術的綜合教程，我們推薦http://java.sun.com/j2ee上的J2EE教程。</P></BLOCKQUOTE>
<P><STRONG>概述</STRONG></P>
<BLOCKQUOTE>
<P>實體bean是設計用來管理關聯資料庫中的資料的。在BEA WebLogic Server 8.1環境下，Entity EJB的開發包括EJB的設計、生成、打包和部署。 </P>
<P>從設計的角度來看，實體bean可以由兩條正交軸線來分類：持久性和讀取。順著持久性這條軸線，有兩種類型的實體bean：Bean管理的持久性（Bean-managed persistence，BMP）和容器管理的持久性（Container-managed persistence，CMP）。如果是CMP，則EJB容器將管理實體bean的持久性。而對於BMP，則由實體bean的開發者通過指定的Java原始碼來管理實體bean的持久性。CMP和BMP之間的選擇是互斥的。順著讀取這條軸線，又有兩種類型的bean：遠端的和本地的。遠端實體bean提供了定位的透明性，並且可以從不同的Java虛擬機上讀取。相反，本地實體bean只能在同一應用伺服器內讀取。本地和遠端之間的選擇不是互斥的，因此可以設計具有雙重介面的bean。</P>
<P>在EJB體系結構規範中，每個實體EJB元件都是由一組指定的Java類和一組指定的XML部署描述符組成。EJB的打包過程包括將所有指定的EJB Java類檔和XML部署描述符檔打包到一個Java Archive (JAR)檔中。如果實體EJB還要依賴於一些Java助手類檔，那麼也可以將這樣的類檔包括到EJB JAR檔中，或者將它們單獨打包到一個不同的JAR檔中。</P>
<P>EJB的部署可分為兩種方式，要麼是在BEA WebLogic Server 8.1內直接部署EJB JAR檔以及所依賴的任何JAR檔，要麼是首先將EJB jar檔和所有依賴的JAR檔打包到一個企業應用歸檔（enterprise application archive，EAR）檔內，然後再在WebLogic Server 8.1內部署這個EAR檔。</P></BLOCKQUOTE>
<P><STRONG>設計EJB</STRONG></P>
<BLOCKQUOTE>
<P>在設計一個實體時，需要考慮的幾個重要選擇是：</P>
<OL>
<LI>是設計一個CMP實體bean還是一個BMP實體bean。 
<LI>是設計一個本地實體bean，還是設計一個遠端實體bean，抑或是設計一個雙重介面的實體bean。 
<LI>是設計一個粗粒度（coarse-grained）的實體bean，還是一個細粒度（fine-grained）的實體bean。 
<LI>是使用資料傳輸物件，還是使用get和set方法來讀取實體EJB中的資料。 <BR></LI></OL></BLOCKQUOTE>
<P>下面將討論在開發實體EJB時要面臨的這些設計選擇。 </P>
<BLOCKQUOTE>
<UL>
<LI><STRONG>CMP與BMP</STRONG><BR>記住，雖然可能存在一些很合理的例外，但是一般情況下我們強烈推薦使用CMP這種設計。至於為什麼要選擇CMP，而不是BMP，這裏有三大主要原因。 </LI></UL>
<BLOCKQUOTE>
<OL>
<LI>與BMP相比，CMP提供了跨多種不同資料庫的可攜性，因為CMP實體bean不包含任何特定於資料庫的持久性原始碼。CMP易於設計、實現和維護。 
<LI>通常，CMP擁有好於BMP的性能，因為EJB容器將自動生成特定於資料庫的原始碼，並且這些原始碼將為目標資料庫而最佳化。 
<LI>CMP通過使用本地介面，使得在相關EJB的網路中程式性地（programmatically）進行瀏覽變得非常容易。 </LI></OL></BLOCKQUOTE>
<UL>
<LI><STRONG>本地介面、遠端介面和雙重介面</STRONG> </LI></UL>
<BLOCKQUOTE>因為CMP通過本地介面管理著實體EJB之間的關係，所以我們強烈建議總是提供一個本地介面。我們認為遠端介面需要的時候很少，但是如果仔細評估一下就會證明遠端介面也是需要的。所以，我們還是設計一個雙重介面吧。 
<P>本地介面通過本地客戶機提供了對EJB的最佳化的讀取；遠端方法呼叫（remote method invocation，RMI）的語義不要求通過本地客戶機、使用本地介面來讀取EJB。 </P>
<P>遠端客戶機與EJB容器位於不同的虛擬機上，它需要RMI和遠端介面來讀取EJB。從理論上講，單單設計一個本地實體存在著一個明顯的缺點，那就是只有在相同應用伺服器內的客戶機才能讀取該實體bean。然而，這只是一個理論上的缺點，因為實際上很少需要從應用伺服器之外讀取實體EJB。 </P></BLOCKQUOTE>
<UL>
<LI>粗粒度與細粒度<BR><BR>這是一個非常有爭議的專題，所以還應根據您個人的經驗小心地評價關於此專題的一些不同觀點。我們的選擇是，實體EJB最常用於表示應用程式業務域中各個實體，所以應該讓實體EJB儘量地細粒度，不過也應將設計限定為僅提供一個本地介面。關於這個問題的爭論始於EJB體系結構規範1.x版本，當時只能通過一個遠端介面來讀取實體EJB。建立在EJB 1.x版本基礎上的任何反對使用細粒度實體EJB的觀點，雖然當時也有合理之處，但是在EJB體系結構規範2.0 中卻已不合時宜了，並且最終遭到反對。請謹記：有些專家可能不同意我們的觀點，所以我們鼓勵您通過實驗進行考證，並在此專題上形成自己的觀點。 <BR>
<LI>資料傳輸物件與Get和Set方法<BR>這又是一個有爭議的專題。我們的觀點如下所述： 
<UL>
<LI>在實體EJB的本地介面中暴露CMP持久欄位的所有get讀取器（accessor）方法。 
<LI>為不屬於實體EJB主鍵的一部分的每個CMP持久欄位建立包裝器（wrapper）set方法，並且在實體EJB的本地介面中暴露這些包裝器方法。使用這些包裝器方法背後的動機是，CMP要求所有持久欄位都具有抽象方法setXXX，如果需要在這些setXXX方法內進行任何驗證，那麼就可以先在包裝器方法內完成驗證，然後再呼叫相應的setXXX方法。如果不需<BR>這樣的驗證，那麼省掉包裝器方法而直接包括setXXX方法也無不可。 
<LI>在某些少見的環境下，可能要求實體EJB有一個遠端介面，這時可以為每個實體EJB定義一個資料傳輸物件，將該資料傳輸物件作為實體EJB的遠端介面中的一個參數，暴露其get和set方法。 </LI></UL></LI></UL></BLOCKQUOTE>
<P><STRONG>生成和打包EJB</STRONG></P>
<BLOCKQUOTE>
<P>在EJB體系結構規範中，每個實體EJB元件都由一組指定的Java類和一組指定的XML部署描述符組成： </P>
<OL>
<LI>一個必需的實現實體bean核心功能的bean類。 
<LI>一個遠端介面、本地介面或雙重介面，該介面為實體bean提供了適當的客戶機視圖。 
<LI>一個遠端主介面（home interface）、本地主介面或者雙重主介面，該介面為實體bean的生命週期管理提供了適當的介面。 
<LI>如果是具有組合主鍵的實體bean，那麼還有一個必需的主鍵類。 
<LI>一個ejb-jar XML部署描述符檔，EJB體系結構規範version 2.0對此作了規定。 
<LI>一個特定於供應商的 weblogic-ejb-jar XML部署描述符檔，BEA WebLogic Server 8.1 對此有規定。 
<LI>如果是CMP實體bean，那麼還應有一個特定於供應商的weblogic-cmp-rdbms-jar XML 部署描述符檔，BEA WebLogic Server 8.1對此有規定。 </LI></OL>
<P>這裏使用的例子實體EJB是一個CMP實體EJB，它有一個遠端介面，它的名稱是AccountEJB。在隨WebLogic Server 8.1一起安裝的示例檔中，即 /weblogic81/samples/server/examples/src/examples/ejb20/basic/containerManaged目錄下，可以找到這個實體EJB。 </P>
<P>實體bean EJB類和介面可以用EJBGen工具來生成。 </P>
<UL>
<LI><STRONG>EJBGen</STRONG><BR>EJBGen是一種EJB 2.0原始碼生成器，它能夠從一個EJB bean類生成本地介面/遠端介面、本地主介面/遠端主介面、主鍵類和部署描述符。在EJB bean類中使用EJBGen標記來指定不同的EJB設計設定（例如，本地/遠端和CMP/BMP）。在WebLogic Server 8.1 SP01 中，EJBGen 類被包括在/weblogic81/server/lib/weblogic.jar中。在WebLogic Server 8.1 SP02 &amp; SP03中，EJBGen類被包括在/weblogic81/server/lib/ejbgen.jar文件中。將ejbgen.jar添加到Classpath中，以便使用EJBGen工具。 
<P>EJBGen通過以下命令來呼叫： </P>
<P><FONT color=#669900>javadoc -docletpath ejbgen.jar -doclet weblogic.tools.ejbgen.EJBGen<BR>&lt;EjbBeanClass&gt;.java </FONT></P>
<P>該命令有一些選項： </P>
<UL>
<LI>d [directory]: 建立EJB類/介面和部署描述符時所在的目錄。 
<LI>descriptorDir [directory]: 建立部署描述符時所在的目錄。 
<LI>wls7: 有了–wls7這個選項，就會生成WebLogic Serve 7.1部署描述符。 <BR>
<P>如果要將更早版本的BEA WebLogic Server部署描述符轉換成WebLogic Server 8.1部署描述符，那麼可以使用DDConverter。 </P></LI></UL>
<LI><STRONG>DDConverter</STRONG><BR>DDConverter是一個命令行工具，用於將更早版本的XML部署描述符（ejb-jar.xml、weblogic-ejb-jar.xml和weblogic-cmp-rdbms-jar.xml）轉換成當前版本的WebLogic Server。 DDConverter可以用以下命令來呼叫： </LI></UL>
<BLOCKQUOTE>
<P><FONT color=#669900>java weblogic.ejb20.utils.DDConverter [options] –d destDir file1 [file2...] </FONT></P>
<P>在這個命令中，file是指包含EJB 1.1部署描述符的一個EJB 1.0部署描述符檔或JAR檔。DDConverter的一部分選項有： </P>
<UL>
<UL>
<LI>d destDir:部署描述符輸出到的目錄。 
<LI>EJBVer output EJB version: 指定輸出EJB版本。缺省版本是2.0。 <BR>在部署EJB之前，必須將其打包到一個JAR檔或EAR檔中。 </LI></UL></UL></BLOCKQUOTE>
<UL>
<LI><STRONG>EJB JAR文件</STRONG><BR><BR>EJB JAR檔的結構由EJB類和介面以及包含部署描述符的 META-INF目錄組成。建立一個目錄source/ejb20/basic/containerManaged和一個目錄source/ejb20/basic/containerManaged/META-INF。將Account.java、AccountBean.java、AccountHome.java 和ProcessingErrorException.java從/weblogic81/samples/server/examples/src/examples/ejb20/basic/containerManaged目錄複製到source/ejb20/basic/containerManaged目錄。將ejb-jar.xml、weblogic-ejb-jar.xml和weblogic-cmp-rdbms-jar.xml從/weblogic81/samples/server/examples/src/examples/ejb20/basic/containerManaged目錄複製到source/ejb20/basic/containerManaged/META-INT目錄。 </LI></UL>
<BLOCKQUOTE>
<P>通過Apache Ant使用編譯好的EJB Java類檔和部署描述符建立一個JAR檔，Apache Ant 是一種基於Java的聯編（build）工具。Apache Ant工具需要一個聯編檔。建立一個帶有目標的build.xml檔（參見清單1），以便編譯EJB原始檔案並利用編譯好的類檔生成一個JAR 檔。</P>
<P>將build.xml檔複製到/source目錄。運行build.xml中的ejb-jar目標。EJB JAR檔生成在source/dist目錄中。EJB JAR可以使用BEA WebLogic appc編譯器來編譯。用appc編譯器來編譯並不是必需的，但這是BEA WebLogic推薦的。 </P></BLOCKQUOTE>
<UL>
<LI><STRONG>appc</STRONG><BR>appc編譯器利用EJB JAR檔生成容器類，並驗證部署描述符。在部署EJB JAR檔之前用 appc編譯EJB JAR類的好處是，這樣可以識別出在部署EJB JAR時可能發生的錯誤。要運行 appc編譯器，應保證weblogic.jar被包括在Classpath中。appc可以通過以下命令來呼叫： </LI></UL>
<BLOCKQUOTE>
<P><FONT color=#669900>java weblogic.appc [options] &lt;jar file or directory&gt; </FONT></P>
<P>appc有一些選項： </P>
<UL>
<UL>
<LI><STRONG>output&lt;file&gt;</STRONG>:指定輸出目錄。 
<LI><STRONG>keepgenerated</STRONG>: 保留生成的.java文件。 
<LI><STRONG>compiler&lt;java&gt;</STRONG>: 將Java編譯器設置成可用。 <BR></LI></UL></UL></BLOCKQUOTE>
<UL>
<LI><STRONG>部署EJB</STRONG><BR><BR>要部署一個實體EJBJAR檔，必須有一個帶有Java命名和目錄介面（Java Naming and Directory Interface，JNDI）名稱的資料源。在我以前的文章（WLDJ, Vol. 3, issue 1）中我已解釋了如何建立連結池（Connection Pool）。用JNDI名 "examples-dataSource-demoPool" 建立一個Tx Datasource。這個JNDI 名應該與weblogic-cmp-rdbms-jar.xml部署描述符檔的 &lt;data-source-name&gt;元素中指定的名稱相一致。 </LI></UL>
<BLOCKQUOTE>
<P>為了部署實體EJB JAR檔，在管理控制臺（administration console）中選擇Deployments&gt;EJB Modules節點。單擊Deploy a new EJB Module鏈結（見圖1）。</P>
<P><IMG height=234 src="http://dev2dev.bea.com.tw/techdoc/images/image02_01.jpg" width=553> </P>
<P>這時會顯示出Deploy an EJB Module表單（見圖2）。在Deploy an EJB Module 表單中選擇 upload your files(s) 鏈結。接著將會顯示一個Upload and Install an Application or Module 表單。選擇一個要上載的EJB JAR檔，並單擊Upload按鈕。</P>
<P><IMG height=290 src="http://dev2dev.bea.com.tw/techdoc/images/image02_02.jpg" width=554> </P>
<P>這時會顯示出Deploy an EJB Module表單。選擇myserver鏈結。接著可以看到myserver目錄中的子目錄列表。單擊upload directory鏈結。在upload目錄中選擇要部署的EJB JAR，並單擊Target Module按鈕（見圖3）。這時會顯示一個Select Targets for this EJB module表單（見圖4）。 </P>
<P><IMG height=255 src="http://dev2dev.bea.com.tw/techdoc/images/image02_03.jpg" width=553> <BR><BR><IMG height=208 src="http://dev2dev.bea.com.tw/techdoc/images/image02_04.jpg" width=553> </P>
<P> </P>
<P>在Select targets for this EJB module表單中選擇一個或多個目標伺服器，並單擊Continue按鈕。在接著顯示出的表單中，在Name欄位中指定用於要部署的EJB模組的名稱（見圖5）。</P>
<P><IMG height=495 src="http://dev2dev.bea.com.tw/techdoc/images/image02_05.jpg" width=553> </P>
<P>單擊Deploy按鈕以部署EJB JAR檔。這樣一來，EJB JAR將被部署到伺服器上，並且有一個EJB節點被添加到EJB Modules節點中（見圖6）。 </P>
<P><IMG height=198 src="http://dev2dev.bea.com.tw/techdoc/images/image02_06.jpg" width=553> </P>
<P>EJB應用程式還可以部署到BEA WebLogic Server上，方法是將EJB JAR檔複製到該應用程式要部署到的那個域上的applications目錄中。 </P>
<P>BEA WebLogic為在 WebLogic伺服器上部署EJB提供了一些建議。</P>
<OL>
<LI>應該將EJB作為企業歸檔應用程式（EAR應用程式）中的一個EJB JAR來部署，以利於應用程式的移植和修改。 
<LI>引用了其他EJB的EJB應該部署在相同的應用程式中；weblogic-ejb-jar.xml中的enable-call-by-reference元素應該設置成True，以獲得更好的性能。 
<LI>部署在WebLogic伺服器叢集上的EJB應該其次地（homogeneously）部署到叢集內每一台受管的伺服器上。如果一個EJB只需部署到叢集內的一台伺服器上，那麼在部署之前應使用appc編譯器先對 EJB進行編譯。 <BR></LI></OL></BLOCKQUOTE></BLOCKQUOTE>
<P><STRONG>結束語</STRONG></P>
<BLOCKQUOTE>
<P>EJB應用程式的開發包括建立EJB類、編譯EJB類和利用編譯好的EJB類建立一個JAR檔，以及將EJBJAR部署到BEA WebLogic Server。通過遵循關於設計、生成和部署EJB JAR到BEA WebLogic Server上的一些建議，EJB應用程式的性能得到了提高。 </P></BLOCKQUOTE>
<P><STRONG>參考資料</STRONG></P>
<BLOCKQUOTE>
<P>l Programming WebLogic Enterprise JavaBeans: http://edocs.bea.com/wls/docs8.1/ejb/index.html </P></BLOCKQUOTE>
<P><STRONG>關於作者</STRONG></P>
<BLOCKQUOTE>
<P>Deepak Vohra 是一名 Web 開發人員，同時也是一名 NuBean 顧問。<BR>Ajay Vohra 是Compuware 公司的一名高級軟體工程師。</P></BLOCKQUOTE>
<P><STRONG>Listing 1: build.xml file</STRONG><BR>&lt;project name="example-entity-ejb" default="all" basedir="."&gt;<BR>&lt;property name="source" value="."/&gt;<BR>&lt;property name="ejb" value="ejb20/basic/containrManaged"/&gt;<BR>&lt;property name="build" value="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{source}/build"/&gt;<BR>&lt;property name="j2sdkee" value="c:/j2sdkee1.3.1"/&gt;<BR>&lt;property name="dist" value="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{source}/dist"/&gt;<BR><BR>&lt;target name="init"&gt;<BR>&lt;!-- Create the time stamp --&gt;<BR>&lt;tstamp/&gt;<BR>&lt;mkdir dir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{build}"/&gt;<BR>&lt;mkdir dir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{build}/META-INF"/&gt;<BR>&lt;mkdir dir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{dist}"/&gt;<BR>&lt;/target&gt;<BR>&lt;target name="ejb"&gt;<BR>&lt;javac srcdir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{source}" classpath="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{j2sdkee}/lib/j2ee.jar"<BR>destdir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{build}" includes="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{ejb}/*.java"/&gt;<BR>&lt;copy todir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{build}/META-INF"&gt;<BR>&lt;fileset dir="META-INF" includes="*.xml" /&gt;<BR>&lt;/copy&gt;<BR>&lt;/target&gt;<BR>&lt;target name="ejb-jar" depends="ejb"&gt;<BR>&lt;jar jarfile="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{dist}/entityejb.jar" includes="META-INF/*.xml,<BR><IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{ejb}/*.class" basedir="<IMG height=12 src="http://dev2dev.bea.com.tw/techdoc/images/s.gif" width=8 align=absMiddle>{build}"/&gt;<BR>&lt;/target&gt;<BR>&lt;/project&gt;</P><img src ="http://www.blogjava.net/kapok/aggbug/6161.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-15 15:26 <a href="http://www.blogjava.net/kapok/archive/2005/06/15/6161.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Web Service进行企业级的门户集成</title><link>http://www.blogjava.net/kapok/archive/2005/06/07/5650.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 07 Jun 2005 02:36:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/07/5650.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/5650.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/07/5650.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/5650.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/5650.html</trackback:ping><description><![CDATA[<P><A href="http://dev2dev.bea.com.cn/techdoc/wlworkshop/20030234.html">http://dev2dev.bea.com.cn/techdoc/wlworkshop/20030234.html</A><BR>在实际工作中构建大型的应用很难。通常你会把门户(portal)和企业集成(EAI)搞混，这样你的工作更难完成。你必须做出一系列的困难决定，很多决定也许会对项目的其余部分产生或好或坏的影响。<BR><BR>在你做出架构性的重要选择之前，都应该深入考虑你构建的应用的每一层( 从前端的负载平衡系统到后端的企业级的系统，也许是全球性的)。即使只是处理这些问题的一个子集（也许"只是"代表那些与门户集成相关的一些问题），你也将面临很多棘手问题：<BR><BR>
<LI>为了得到最好的效果，我是不是应该把我的web层和一些流程组件连接起来，让这些组件充当工作流和应用集成层的业务代理，让集成层处理EAI的复杂问题？是不是每次都可以按这样的套路进行呢？<BR>
<LI>我的web层和工作流层是不是应该采用松耦合(例如，使用JMS)，或者在某种情况下，为了利用BMP(Business Process Management)的API提供的工作列表(worklist)功能的好处，是否可以不用松耦合?<BR>
<LI>在创建统一用户资料(Unified User Profile)时，我如何精确的和CRM，ERP和安全系统打交道?<BR>
<LI>门户内容管理参考实现是否提供了足够多的功能？我是不是需要评估一下第三方的解决方案？<BR>
<LI>我们是否应该利用新的证书映射提供者(credential mapping provider)通过J2EE CA Adapters传递认证信息？还是用Web services的SAML (Security Assertion Markup Language)？我们的第三方单点登陆(Single Sign-On (SSO))安全系统是否支持这些机制？我有没有SSO？我是否需要一个呢？<BR><BR>幸运的是，这只是一篇杂志中的文章，所以我们可以先把一些问题放在一边，以利于我们集中精力，减少篇幅。本文描述了WebLogic 7.0 Enterprise Platform里可以用来在门户中用Web services进行集成的一些工具和技术。在一个简要的原型系统例子中我们对这些技术进行了演示。这里我定义的门户集成是指：把从不同的资源（通常是外部的）中获得的信息，通过检索、转换、组织、显示，形成统一的、个性化的整体。本文主要是讨论Web service，所以只是简要介绍这些门户集成功能中对第三方的内容和文档的管理的功能，该功能在企业架构中应当被考虑。我将简要介绍以下内容：<BR><BR>
<LI>J2EE CA 应用视图(J2EE CA Application Views)<BR>
<LI>Workshop Application集成控制(Workshop Application Integration Controls )<BR>
<LI>Liquid 数据视图和源 (Liquid Data Views and Sources)<BR>
<LI>应用集成和Web services 工作流插件(Application integration and Web services workflow plug-ins) <BR>
<LI>统一用户资料框架(The Unified User Profile Framework )<BR>
<LI>Web services Portlet向导 (The Web Services Portlet Wizard )<BR><BR>以上内容为使用Web services进行松耦合的企业门户集成提供了非常强大的框架。请注意，本文假设读者对WebLogic Portal 4.0和Integration 2.1非常熟悉，在http://e-docs.bea.com和BEA WebLogic Developer's Journal杂志中都有关于WebLogic Portal 4.0和Integration 2.1的丰富的资料。<BR><BR><B>门户集成（Portal Integration）：一个原型示例</B><BR><BR>我们的例子是一个IT技术支持部门的案例管理门户。问题单根据技术支持工程师的专业（例如数据库，用户界面，事务管理）和技术等级(一级，二级等)分发。每个工程师有一个相关的资料，资料同时存在于一个安全的关系数据库和一个外部的CRM系统中，资料中有该工程师的专业和技术等级信息，也可能有工程师的管理者---高级工程师的信息。高级工程师可以分析下属的案例历史，包括完成案例的平均时间和案例数量增长的百分比。每个案例的实际数据存在两个外部问题单系统中，一个系统相对较新，使用了Web services，另一个系统较旧，有一个专有界面。除了核心的案例管理功能，每个工程师的门户都可以个性化，使用另外的含有公开技术论坛的Portlet，含有内部错误报告更新的Portlet，以及类似的Portlet。<BR><BR><B>应用视图（Application Views）：实际上所有的内容都可以展示</B><BR><BR>J2EE Connector Architecture (J2EE CA) 适配器是连接J2EE组件和外部企业信息系统(EIS)的桥梁。EIS所需的适配器接口经常使用专有的协议、数据格式和认证机制。WebLogic J2EE CA适配器处理协议转换，也常用于处理数据格式的转换，或者利用WebLogic里的证书映射提供者传递认证信息到EIS中，如果EIS含有XA，那么XA事务也可以传递。<BR><BR>J2EE CA 1.0规范没有规定适配器的标准的接口(只提供了一个可选的接口)，也没有规定一个标准的信息格式或者EIS发出的异步事件。1.5规范(现在是建议最终草稿版的第二版)修补很多类似的漏洞，1.5版规范会包括在J2EE 1.4中。<BR><BR>WebLogic 集成应用视图框架(WebLogic Integration Application View Framework)在J2EE CA 适配器之上提供了一层，弥补了1.0规范中的不足(1.5规范中的改进在此由应用视图提供)。当你创建一个应用视图的时候，你也指定了一个和相关业务服务以及EIS中的事件相对应的XML schema，当与请求schema相应的XML文件传过来时，服务被激活，返回结果根据响应schema以相应的XML文件返回。事件以异步的方式分发到客户端，同样是按照协商好的schema，以 XML文件的形式传递。我们通过基于浏览器的应用集成控制台（Application Integration console）来创建应用视图，在控制台里把服务和事件同适配器连在一起，指定相应的schema。<BR><BR>应用视图服务可以被激活，事件监听器使用的是应用集成API。应用视图可以在业务流程管理(BPM)工作流中使用，也可以做成Web services，相应的技术稍后介绍。<BR><BR>在我们的案例管理门户示例中，我们把遗留系统的问题单和CRM系统的专有界面发布为应用视图，每个视图提供与相关系统对应的一套业务服务和异步事件。<BR><BR><B>Workshop应用集成控制：应用视图发布为Web service</B><BR><BR>使用WebLogic Workshop的IDE简化了Web service的开发、部署和调试。 Workshop还提供了透明信息缓冲和带对话功能的有状态Web service。WebLogic Workshop的开发人员可以利用一些特殊的控制(controls)轻松的把后端的J2EE组件发布为Web service。其中的一个控制允许Workshop的开发人员将应用视图服务和事件发布为Web service。这样，开发人员就可以通过Web service和所有的外部系统进行交互。<BR>在我们的案例管理门户示例中，我们使用Workshop应用集成控制把我们的遗留系统的专有界面对应的应用视图发布为Web service，这样我们面对的两种系统就有相同的风格。 <BR><BR><B>Liquid Data：实际上所有的事情都可以转变为其他的形式</B><BR><BR>Liquid Data，是WebLogic Platform中新的功能强大的组件，提供在众多的数据源(应用视图、数据视图、FTP站点、Web services等)之上创建视图的能力。这些视图可以串在一起(例如，视图的视图)。Liquid Data一旦定义，可以对这些视图创建预先存储的和动态的查询。查询可以通过已经提供的EJB和基于JSP标记库的API来配置和激活。查询也可以发布为Web services。Liquid Data的理论基础建立在XQuery(http://www.w3.org/TR/xquery)规范的一个实现之上。Data View Builder包括Liquid Data的IDE(集成开发环境)和类似Workshop的GUI（图形用户界面），你可以创建针对数据源的视图，针对视图的预先存储的查询(开发人员可以使用XQuery语法来手工编写高级查询)。Data View Builder还提供测试和调试这些视图和预先存贮的查询的能力。<BR><BR>本文的目的之一就是介绍一种关键能力，即创建基于已有的应用视图和Web services 的Liquid Data复合视图。一个视图可以传递特殊的Portlet或用户资料(User Profile)所需的信息，转换需要调整的信息，相应的设置可以公开进行，并不需要修改实际的应用视图或Web services(或文件，数据库等等)。<BR><BR>在我们的案例管理门户示例中，可以创建支持工程师的统一用户资料视图，该视图对应于安全关系数据库和含有适配器的可以发布为CRM系统的应用视图。同样，可以创建一个或多个案例信息视图来映射基于Web service的问题单和遗留系统，遗留系统的接口通过一个应用视图发布。<BR><BR><B>工作流（Workflow）和Web services：BMP(业务流程管理)集成<BR></B><BR>工作流控制着企业业务处理的流程，它通过集成插件接入点和实际的业务逻辑紧密地联结在一起。工作流通过BPM Studio GUI创建，Studio的界面有些像"Visio"，可以通过拖放的方式创建工作流。从工作流中可以直接呼叫应用视图服务（Application view services），应用视图事件可以通过应用集成插件来触发工作流事件节点。同样，从工作流事件中可以通过一个可以从BEA的开发人员站点 (http://dev2dev.bea.com) 下载的插件调用Web services，dev2dev的Web service插件提供一个GUI，允许开发人员把应用视图服务发布为Web service（Workshop AI 控制的一个有限子集）。<BR><BR>在我们例子中的portal通过在流水线组件中调用BPM API与问题票务分派工作流打交道。一个工作流任务从两套问题票务系统中获取问题票务信息，该工作流在较新的系统中激活适当的Web service，在另一个系统中激活应用视图服务（application view services）。该工作流可以直接在BPM Studio GUI中创建，不需要任何手工编程。<BR><BR><B>统一用户资料（Unified User Profile）：分类化和个性化集成</B><BR><BR>门户中的包含用户资料的属性位于一个预先定制好的关系数据库中。门户的个性化和分类化组件(这些组件用来判断你是谁，是什么，有什么兴趣等等)使用用户的资料属性。你可以通过门户的统一用户资料(UUP)框架来把用户资料扩展为企业级的资料。该框架允许一个开发人员从另一个可选资源(例如，LDAP，CRM/ERP系统)中把用户属性插入进来。简而言之，开发人员只要执行一个EntityPropertyManager EJB，就可以使用它来获得扩展的用户属性。这个EJB以ProfileManager EJB为基准(你在这个EJB的部署描述环境中加入你的EntityPropertyManager信息)。<BR><BR><B>现在你开始使用EntityPropertyManager EJB，那你实际上要使用什么技术来获得用户的属性?</B><BR><BR>· 如果外部系统的Web service是处于激活状态，或者同样的你使用Workshop、Liquid Data或者Web service BMP插件的GUI界面发布的Web service的话，你可以使用JAX-RPC从Web service中获得信息。<BR>· 你可以使用Liquid Data Query API来把外部系统发布为Liquid Data View。<BR>· 如果外部系统有相应的由应用视图（Application View）公布的J2EE CA 适配器的话，你可以使用应用集成API。<BR>· 你可以直接和J2EE CA 适配器交互。<BR>· 你可以使用私有的方法。<BR><BR>示例中的门户根据Unified User Profile中的专业和资历来进行问题单的分配。某个专业的工程师被指派为管理者的同时也成为一个管理权力集团(Management Entitlement Segment)的成员，可以访问Engineer Case History Portlets，这些portlet允许管理人员根据某个工程师过去的案例处理情况来分析他或她的工作表现。就像刚才讲的，本例中的EntityPropertyManager EJB可以使用JAX-RPC来获得我们的用户信息，发布为Liquid Data Web Services View。 
<P></P>
<P><B>Web Services Portlets：web层的集成</B><BR><BR>Web Services Portlets，如同它的名字所暗示，使用Web Services，然后以内容的形式把结果显示出来。这些portlet可以用Portal EBCC Portlet Wizard快速开发，从非常基本的portlet类型到和使用用户定义的数据类型进行动态的、异步交互的portlet类型。Web Services Portlets也可以参与到Workshop类型的交互中。<BR><BR>当今大多数精心设计的Web应用都采用Model 2 Web层结构模式。被广泛使用的Apache Jakarta的"Struts"就是这种模式的很好应用。门户的Webflow/Pipeline框架的工作模式与此类似。Model 2模式的基本原则是：分离业务(controller, model, and view)和"view"(我们的例子中是portlet)的分离，view的主要业务是显示现在相关的model的内容。从一个portlet中激活和使用一个或多个Web Service似乎会和以上原则冲突，实际上有时会有冲突发生。不过，某些情况下，不会有冲突发生：<BR><BR>· 使用的portlet单独存在(一个单独的"小型应用")。<BR>· Web Service提供model的当前状态(J2EE设计模式中Front Controller的View Helper策略)。<BR>· Web Service激活的结果的格式是portlet用户界面的形式。<BR><BR>在我们的原型示例系统中，一个支持工程师专门接收他们使用的关系数据库的厂商发布的技术公告。该公告在门户中的一个portlet中显示。这是一个单独的服务，和门户中的其他portlet无关，而且信息是调用外部的Web Service获得的。在这种情况下使用Web Services Portlet的另一个主要原因是从这个Web Services获取信息就是用户接口。<BR><BR><B>总结</B><BR><BR>本文介绍了WebLogic Enterprise Platform的一些在构建企业门户解决方案的时候可以使用的功能。本文的目的不是提供一个单一的，完整的结构(类似"宠物商店"的门户集成简单示例)，也不是暗示在所有情况下(或者大多数情况下)必须使用某些工具和技术。在一个给定的环境中有太多的因素需要考虑。使用Web Service进行比较明智的门户集成时，可以创建一个非常灵活的架构。但是如果不进行全盘考虑，一些重要的问题(例如性能、可扩展性、安全和事务协同性)就可能发生。这一点上，Web Service和其他的技术一样。一个有经验的架构师明白这一点，所以既不会对Web Service过分狂热，也不会因为Web Service的缺点而怀疑Web Service。<BR><BR><B>关于作者</B><BR><BR>Steve Buzzard 是Anexinet公司(<A href="http://www.anexinet.com/" target=_blank>http://www.anexinet.com/</A>)的 J2EE 应用架构师，该公司位于费城，是一家领先的咨询公司。 Steve 有17年的软件开发经验，从1998年以来，几乎一直使用WebLogic技术来进行开发。 </P></LI><img src ="http://www.blogjava.net/kapok/aggbug/5650.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-07 10:36 <a href="http://www.blogjava.net/kapok/archive/2005/06/07/5650.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 JMS 在集群应用程序中分配任务</title><link>http://www.blogjava.net/kapok/archive/2005/06/07/5648.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 07 Jun 2005 02:07:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/07/5648.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/5648.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/07/5648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/5648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/5648.html</trackback:ping><description><![CDATA[废话就不要看了，看看线程和command的部分设计就可以了，还不错的想法。<BR><BR><A href="http://dev2dev.bea.com.cn/techdoc/webser/2005060104.html">http://dev2dev.bea.com.cn/techdoc/webser/2005060104.html</A><BR><BR><BR>在请求驱动的环境中解耦合和延迟处理是创建健壮和可伸缩的分布式应用程序的关键战略之一。许多服务都单独依赖于集群来确保可伸缩性，但是当新发现的需求使应用程序的复杂性增长时，它们常常会遇到麻烦。 
<P>　　尽管服务器集群是推动可伸缩性的基本技术，但是当所有的处理都同步完成时，它可能变得很低效。吞吐量可能会增加，但是响应性却会变得不可救药。 </P>
<P>　　在本文中，我讨论了异步处理，并举例说明了，巧妙的任务管理会如何提高您应用程序的性能、可用性、可伸缩性和可管理性。我们将以一种高度可配置的方式，创建一个一般的任务分配框架，该框架可以发送任何任务给您的集群中的一台或每台服务器。通过使用多态和 Java 消息服务（ Java Message Service ， JMS ），我们的框架将实现著名的 Command 模式。</P>
<P><STRONG>解耦合在实际中的意义 </STRONG></P>
<P>　　当服务器收到客户端请求时，它通常需要在返回响应之前执行几个单独的任务。解耦合（ decoupling ）意味着不会一次执行所有的任务，而是把一些任务放入队列并异步地进行处理。因为排队通常是一项低开销的操作，同步的请求会更快地结束。 </P>
<P><STRONG>解耦合的优点</STRONG></P>
<P>　　按照顺序和并行地处理任务，通常会比随机地处理它们要更加高效（客户端偶然发出请求的时候）。正面的影响比随即表现出来的要大。理论上，解耦合可以提高以下各个方面的性能： </P>
<UL>
<LI><STRONG>健壮性 </STRONG><STRONG>： </STRONG>提高，因为请求将依赖的可能失效的过程更少。 
<LI><STRONG>响应性 </STRONG><STRONG>： </STRONG>请求的部分后处理减少了接收请求和返回响应之间的时间。 
<LI><STRONG>可伸缩性 </STRONG><STRONG>： </STRONG>所有解耦合后的过程在复杂性方面可能会增加，但不会有降低响应度的危险。 
<LI><STRONG>可用性 </STRONG><STRONG>： </STRONG>可以在服务器永远不知道故障原因的情况下处理故障。 </LI></UL>
<P>　　在子系统不可用的情况下，配置自动重试要更加容易。 </P>
<P>　　自然，理论与实践之间的差别在每个应用程序上的体现各不相同。然而，显然，几乎每种实现都至少具有前述的某些优点。 </P>
<P><STRONG>解耦合的缺陷</STRONG></P>
<P>　　和大部分好东西一样，解耦合也存在缺点。其中最严重的缺点之一就是，如果您不能确保您拥有足够强大的硬件来清空繁忙的处理队列，那么您可能会发现，它的可用性实际上降低了。如果进入的异步请求比您的系统能够处理的要多，队列就会非常迅速地增长。您必须注意设计过程，而对队列实行自动监控无疑是可取的做法。另一个明显的问题是，在请求驱动的环境中，大多数过程都不是很适合于解耦合。事实上，大多数处理都可能被要求返回响应。有时，它会需要一些开箱即用的思想，甚至可能是您为您的客户端所提供的服务方式的变化。 </P>
<P><STRONG>哪些过程可以解耦合</STRONG></P>
<P>　　从纯技术的角度来讲，几乎所有的过程都可以解耦合。例如，您可以把一个所购买物品及客户详细信息的清单放入队列，从而可以解耦合一个订单事务——异步处理将负责余下的工作。不足之处在于您不能在响应中包含任何处理细节。因此，谨慎地预先验证数据是很重要的，这样可以确保不会出现问题。 </P>
<P>　　一种越来越流行的实现是，马上把请求放入队列，然后持续轮询服务器，以便了解何时可以获得响应。尽管这种方法在本质上实际是同步的，而且不会增加请求的处理时间，但是它在心理上具有优势，因为可以在轮询期间显示进度条。 </P>
<P>　　除了解耦合完整的业务逻辑（这是一个巨大的难题）之外，更少的集中处理，比如日志记录和发送电子邮件，是可以考虑的良好选择。当性能变得及其重要时，没有理由让客户端等待这种任务的完成。特别的电子邮件是用于解耦合的一个良好选择。让我们做进一步的了解。 </P>
<P><STRONG>实例研究:异步的电子邮件 </STRONG></P>
<P>　　以传统的方式发送电子邮件（作为同步请求的一部分）会引起一些问题。首先，连接到电子邮件服务器需要一次网络往返，速度可能会很慢，尤其是在服务器非常繁忙的时候。过载的电子邮件服务器甚至可以使依赖于电子邮件的服务暂时不可用。 </P>
<P><STRONG>XA </STRONG><STRONG>事务支持 </STRONG><BR>　　另一个显而易见的问题是，电子邮件服务器通常在本质上是非事务性的。当事务被回滚时，这可以导致出现不一致的通知——把一条消息放入队列之后不能取消它。幸运的是， JMS 支持事务，而且可以通过把消息的发送延迟到提交底层事务的时候来解决这个问题。 </P>
<P>　　考虑访问数据库和事务感知的 JMS 时，您将需要使用 XA 和两阶段提交（ 2PC ）事务。可以使用非 XA 资源来模拟 XA ，但是您可能会得到不一致的数据。启用 XA 只是一个配置问题，而且通常不需要修改代码。参见 WebLogic 文档以获得相关的详细信息。 </P>
<P><STRONG>通过 </STRONG><STRONG>JMS </STRONG><STRONG>发送电子邮件 </STRONG><BR>　　为了使用 JMS 来发送电子邮件，我们需要配置 JMS 组件（比如， JMS 服务器， JMS 队列和连接工厂）。我们还需要编写一个消息驱动 Bean （ Message Driven Bean ， MDB ）来执行信件的实际发送。当我们想要在我们的代码中发送电子邮件时，需要创建一条包含信件的属性和内容的 JMS 消息。之后，我们把它发送给处理队列。 </P>
<P>　　这样做的工作量很大！幸运的是， BEA WebLogic JMS 为我们提供了创建一个可以解耦合几乎任何过程的框架所需的全部内容。 </P>
<P><STRONG>用于异步执行的框架 </STRONG></P>
<P>　　是动手看看一些代码的时候了。我们将创建一个框架，这个框架支持在集群中的一台或所有服务器上，异步地执行代码的任何部分。实现起来的确需要花费些力气，但是一旦框架完成，异步执行就是再容易不过的事情了。 </P>
<P>　　这个思路是编写一些类，这些类包含一个带有可运行代码的公共方法和另一个用于初始化参数的方法——可能是构造器。封装在 JMS 对象消息中之后，这些预先编写好的类的实例（命令消息）就被发送给在您的服务器上配置的 JMS 队列。至此，消费者把它们取出，然后异步地执行它们（参见图 1 ）。 </P>
<P><IMG height=302 src="http://dev2dev.bea.com.cn/techdoc/webser/images/images0505310401.jpg" width=389> </P>
<P>　　让我们逐个看看这个框架的所有部分： </P>
<UL>
<LI><STRONG>JMS </STRONG><STRONG>队列 </STRONG><STRONG>： </STRONG>应该在每台服务器上配置一个用于接收命令消息的 JMS 队列。还应该为重复保存故障消息配置错误队列。 <STRONG></STRONG>
<LI><STRONG>JMS </STRONG><STRONG>连接工厂 </STRONG><STRONG>： </STRONG>为了支持事务性行为的运行时选择，应该配置两个连接工厂：一个支持 XA ，而另一个不支持 XA 。 <STRONG></STRONG>
<LI><STRONG>命令对象接口 </STRONG><STRONG>(CommandMessage) </STRONG><STRONG>： </STRONG>这是一个所有命令对象都需要实现的简单 Java 接口。它扩展了 java.io.Serializable 接口，该接口对于在 JMS 对象消息中嵌入我们的命令来说是必需的。现在，因为我们想要在不知道命令的确切类型的情况下运行它们，我们还要实现 java.lang.Runnable 接口，稍后把它们简单地转换为 Runnable 对象，并执行它们的运行方法。我们在不知道我们运行的确切内容的情况下运行了代码。这是最理想的多态。 <STRONG></STRONG>
<LI><STRONG>命令执行程序 </STRONG><STRONG>(CommandExecutionManager) </STRONG><STRONG>： </STRONG>我们将使用一个 MDB 来处理命令。实例池化防止了 JMS 初始化重复出现，这使得 MDB 成为功能非常强大的消息监听器，非常适合于这项任务。编写 Bean 类不需要很大的工作量，我们只需要在 onMessage 方法中编写数行代码（参见清单 1 ）。 </LI></UL>
<P>　　这样就把收到的消息传递给一个 ObjectMessage ，获得嵌入的命令对象，然后执行它的运行方法。通过在 config.xml 文件中，把队列的重新发送限制设置为一个大于 0 的值，您可以配置一个重试计数器。从您的命令对象抛出一个运行时异常，便可触发重新发送的动作。此外，通过配置重新发送延迟，您还可以控制重试的频率。 </P>
<P><STRONG>用于发送消息的一个帮助器类 (TaskDistributor) </STRONG></P>
<P>　　从技术上说，这个部分并不是完全必要的，每次都可以手动进行 JMS 排队。然而，这是一个冗长乏味的过程，而且实际上是帮助器使这个框架变得如此实用。帮助器是一个常规的 Java 类，带有用于对命令消息进行排队的静态方法。您可以针对处理不同的场景编写单独的方法，但是为了简明起见，我选择了编写一个可以处理大多数情况的方法： </P>
<P>static void execute(CommandMessage cm, long delay, b oo lean runEverywhere, b oo lean persisted, b oo lean <BR>enableXA, int priority) </P>
<P>　　这个静态方法带有几个用于精确执行控制的参数。让我们逐个讨论这些参数： </P>
<UL>
<LI><STRONG>CommandMessage cm </STRONG><STRONG>： </STRONG>一个命令消息实例。 
<LI><STRONG>long delay </STRONG><STRONG>： </STRONG>代表发送属性的时间，借助 weblogic.jms.extensions.WLMessageProducer 类进行设置。这样，就可以在夜间或者其他方便的时间执行命令。接受一个 Date 对象也是可以的。 
<LI><STRONG>b oo lean runEverywhere </STRONG><STRONG>： </STRONG>决定是否发送要执行的消息给集群中的一台随机选中的访问器或者所有的服务器。 
<LI><STRONG>b oo lean persisted </STRONG><STRONG>： </STRONG>将通过使用队列发送程序的 setDeliveryMode 方法选择发送模式。应该始终保持业务关键型的消息，从而在访问器崩溃的时候，这些消息不会丢失。然而，持久性始终是以性能损失为代价的，这也应该纳入考虑的范围内。 
<LI><STRONG>b oo lean enableXA </STRONG><STRONG>： </STRONG>将选择方法是否使用支持 XA 的 JMS 连接工厂。此参数设置为 true 时，排队将参与底层事务（如果存在的话），在提交事务之前不会对消息进行排队。 
<LI><STRONG>int priority </STRONG><STRONG>： </STRONG>决定消息的 JMS 优先级。在发送之前，将使用给定的值调用 javax.jms.Message 类的 setJMSPriority 方法。有效的范围是 0-9 。对于大多数应用程序来说，给命令消息指派不同的优先级似乎有些过头，但是出于完整性方面的考虑，我还是在这里包括了这个选择。 </LI></UL>
<P>　　应该针对您的特定执行的需要，来量身打造 TaskDistributor 帮助器类的实现。在本文中，要包含一个例子似乎太长了，但是您可以从 WLDJ Web 站点 <A href="http://dev2dev.bea.com.cn/techdoc/webser/www.sys-con.com/wldj/sourcec.cfm" target=_blank>www.sys-con.com/wldj/sourcec.cfm</A> 下载一个。还可以添加几个额外的参数来控制更高精度的执行，但是另一方面，您可能已经满足于较少的几个选项。 </P>
<P><STRONG>使用异步执行 </STRONG><STRONG></STRONG></P>
<P>　　框架完成之后，我们就可以开始实现我们的命令消息了。让我们看一个简单的例子。首先，我们需要创建一个代表我们的命令消息的类（参见清单 2 ）。为了调用执行，我们使用了 TaskDistributor 类（参见清单 3 ）。 </P>
<P>　　当调用例子中的执行方法时，一个包含 DistributedLogger 类实例的 ObjectMessage （ JMS 优先级被设置为 4 ）将在 1 秒延迟之后，被发送给集群中所有的服务器。随后，记录程序将在所有的服务器上打印一个字符串给 stdout 。借助已经完成的框架，异步执行变得非常易于实现，完全没有障碍。节点到节点的通信变得前所未有的容易。 </P>
<P><STRONG>容器托管的任务分配 </STRONG><STRONG></STRONG></P>
<P>　　我们可以通过使用池化线程和虚拟内存队列，来创建一个类似的服务，以处理异步请求。然而，我们强烈建议让应用服务器来管理所有的线程。 </P>
<P>　　此外，因为 JMS 为我们提供了一个非常简洁和灵活的解决方案，没有理由不让我们的服务器来处理错综复杂的过程。事实上，我们可以调用这个方法容器托管的任务分配（ Container-Managed Task Distribution ）。 </P>
<P><STRONG>性能问题 </STRONG><STRONG></STRONG></P>
<P>　　BEA WebLogic 可以处理很大的消息量，而且性能通常不是问题。尽管如此，当生产非常大量的命令用于处理时，还是推荐使用非持久性的消息和流水线操作。另外，消息流控制可以缓解：服务利用率的暂时高峰所导致的消息处理消耗过多资源。 </P>
<P><STRONG>并行处理 </STRONG><STRONG></STRONG></P>
<P>　　使用 MDB 的一个巨大优点是，它们可以自动地并行处理消息。您可以通过显示池化消费者 Bean 的数量来调整消耗处理资源的数量。 </P>
<P>　　WebLogic 提供大量有价值的 JMS 扩展和配置选项——它们中有许多可以用在任务分配的各种实现中。选择和优化用于分页、重新发送、持久性和调节（流控制）的 JMS 参数时，应该格外注意。 </P>
<P>　　JMS 是一个非常完善的服务，对其功能进行仔细研究是值得的。想要了解有关提高性能的更多信息，请参见 WebLogic JMS 性能指南。 </P>
<P><STRONG>结束语 </STRONG><STRONG></STRONG></P>
<P>　　我们已经讨论了解耦合和异步的消息收发。作为一条经验法则，我们可以说，处理异步请求的服务器，比专门处理同步请求的服务器的执行效率要高。尽管解耦合并不总是件容易的事情，或者甚至不总是个可行的方法，但在考虑周全地实现时，它仍然是一种功能非常强大的机制。我们获得的不仅仅是几个与性能相关的优点，而且能够设计更加灵活的应用程序。 </P>
<P>　　BEA WebLogic JMS 远远不止是个用于数据传输的服务。除了可配置性非常强之外，它还提供许多有用的功能，比如自动重新发送，消息的持久化，可调度性， XA 支持，调节，瞬时分页和循环故障消息的重定向。通过利用这种强大的通用性，我们可以创建一个功能强大而且可以扩展的框架，来处理需要进行异步处理的所有情形。 </P>
<P><STRONG>关于作者 </STRONG></P>
<P>　　John-Axel Strahlman 是 Sanda Interactive Ltd ( <A href="http://www.stcinteractive.com/" target=_blank>www.stcinteractive.com </A>) 的创立者和 CEO 。 Sanda Interactive Ltd 是一家软件咨询公司，总部位于芬兰的 Espoo 。他是一位分布式系统专家，为他的客户的项目提供咨询已经超过 5 年。（ <A href="http://sys-con.com/author/?id=5295" target=_blank><EM>更多信息 </EM></A>）</P>
<P><STRONG>清单 1 ：命令处理器 MDB </STRONG></P><PRE class=code>public void onMessage(Message msg) {

  ObjectMessage om = 
  	(ObjectMessage) msg;

  Runnable command = 
  	(Runnable) om.getObject();

  command.run();
}

</PRE>
<P><STRONG>清单 2 ：命令类示例 </STRONG></P><PRE class=code>public class DistributedLogger
  implements CommandMessage {

  private String text = null;

  public void setText(String text) {
    this.text = text;
  }

  public void run() {
    System.out.println(text);
  }
}


</PRE>
<P><STRONG>清单 3 ：框架使用示例 </STRONG></P><PRE class=code>DistributedLogger logger =
  new DistributedLogger();

String text = 
  "Hello asynchronous execution!"

logger.setText(text);
TaskDistributor.execute(
  logger, //Command instance
  1000,   //delay
  true,   //runEverywhere
  false,  //persisted
  false,  //enableXA
  4);     //delay.

</PRE><STRONG>原文出处 </STRONG><STRONG></STRONG>
<P><A href="http://sys-con.com/story/?storyid=48222&amp;de=1" target=_blank>http://sys-con.com/story/?storyid=48222&amp;de=1 </A></P><img src ="http://www.blogjava.net/kapok/aggbug/5648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-07 10:07 <a href="http://www.blogjava.net/kapok/archive/2005/06/07/5648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>门户规则服务 </title><link>http://www.blogjava.net/kapok/archive/2005/06/06/5613.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 06 Jun 2005 07:48:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/06/5613.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/5613.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/06/5613.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/5613.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/5613.html</trackback:ping><description><![CDATA[<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200404173.html">http://dev2dev.bea.com.cn/techdoc/wlportal/200404173.html</A><BR><BR>WebLogic Portal </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">通过一套组件提供了一种健壮的个性化解决方案，这些组件提供了运行时服务，用于向浏览</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Web</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">站点的最终用户交付个性化的内容。这些个性化组件利用业务规则来匹配具有合适内容的用户和用户组。围绕业务规则的逻辑是个性化处理过程的关键部分。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">1.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Weblogic Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> Rules Management Framework</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">提供了运行时功能，用于提供基于具体规则的个性化内容。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">2.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Manager EJB </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">是用于访问底层</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> <SPAN>BEA Rules Engine</SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的一个可伸缩的、无状态的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> J2EE </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">入口点。它提供了执行用户定义的业务规则所必需的运行时服务。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">3.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">两个</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控件：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesManagerControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">和</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，提供了访问</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Manager EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的方便手段。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR>Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">自带了一些规则，例如内容选择器和活动（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">campaigns</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）。用户可以手工建立自定义规则。在把规则部署到</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">之前，可以使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Expression</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">包，或者</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Xml Spy </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">来验证这些规则的有效性。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">业务规则可以动态地加载和修改，这样，通过</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Workshop IDE</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，提供了一种测试已部署应用程序中规则的有效方式。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">规则引擎的工作原理</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则引擎（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Engine</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）的运作机制是在内存中向对象应用一套规则。首先内存使用来自调用对象的输入，例如用户档案请求会话。这样，在任何规则实际激活之前，在内存中就已经有了一份用户档案的内容。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则只能在一个上下文环境中执行，上下文环境把规则集和内存关联起来。该环境提供了到</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Engine</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的接口，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Engine</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控制着应用程序的规则部分与内存之间的关系。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">内存由生产规则（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">production rules</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）负责操作，生产规则包含在规则集里。，依照规则的左半边（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">left-hand sides</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">LHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）针对内存中的对象进行计算。如果内存中的对象与</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">LHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">中描述的模式匹配，就会触发规则的右半边（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">right-hand side</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）指定的操作。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">此外某些操作可能会在内存中加入新的对象。例如，规则</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> Classifier </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对用户年龄进行测试，如果</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> USER.age &gt; 45</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，就在内存中加入一个新的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Classification </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">生产系统的运行，要执行以下操作：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">1.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">匹配</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">: </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">估计规则的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">LHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，判断哪个规则与当前内存中的内容匹配。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">2.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">冲突解决：选择一个</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">LHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">匹配的规则。如果没有规则匹配，就停止解释。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">3.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">操作</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">: </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">执行选中规则</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RHS</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">中指定的动作。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">4.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">返回第</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">1</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">步。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则会一直在内存中执行，直到冲突解决集变为</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">0</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">时才停止（也就是没有规则能激活了）。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules Engine</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">停止之后，规则管理器组件会返回一个对象列表，列表中包含内存中仍然存在的对象。一个可能的场景就是，还剩下一个类型为“</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Classification</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">”或“</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">ContentQuery</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">”的对象。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR>Rules Manager</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">接着对剩下的对象进行迭代，用可选的对象过滤器过滤它们。过滤器可以有选择地忽略某些对象或者对某些对象进行变换。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">使用门户规则服务</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">要使用门户规则服务（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal Rules Service</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">），有几个步骤。首先，必须预先建立规则，然后把规则部署到一个正在运行的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Weblogic</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">服务器实例中。接着，规则被计算，而计算结果在返回用户之前，有可能先被过滤。最后，用户在应用程序代码里利用经过规则处理的结果。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">建立规则集</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则集是符合特定模式的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">XML</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件（详见</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">XmlSpy</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的链接</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">)</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。规则“语言”实际就是</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">表达软件包的使用，可以扩充以满足规则引擎的额外要求。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal vAlign=top bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;cr:rule-set is-complete="true" xmlns="http://www.bea.com/servers/p13n/xsd/expression/expressions/2.1.1"</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>xmlns:cr="http://www.bea.com/servers/p13n/xsd/rules/core/2.1.1"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>xmlns:literal="http://www.bea.com/servers/p13n/xsd/expression/literal/1.0.1"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>xmlns:string="http://www.bea.com/servers/p13n/xsd/expression/string/1.0.1"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>xsi:schemaLocation="http://www.bea.com/servers/p13n/xsd/rules/core/2.1.1 rules-core-2_1_1.xsd"&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&lt;cr:rule is-complete="true"&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; &lt;cr:name&gt;AddInteger&lt;/cr:name&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; &lt;cr:description&gt;Simple value test &lt;/cr:description&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; &lt;cr:conditions&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;equal-to&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&lt;variable&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;type-alias&gt;java.lang.String&lt;/type-alias&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN lang=SV style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;/variable&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;literal:string&gt;Nederland&lt;/literal:string&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;/equal-to&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/cr:conditions&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;</SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &lt;cr:actions&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;cr:add-object&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; &lt;type-alias&gt;java.lang.Integer&lt;/type-alias&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <SPAN lang=SV style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;arguments&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &lt;literal:string&gt;10&lt;/literal:string&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;/arguments&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&lt;/cr:add-object&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/cr:actions&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&lt;/cr:rule&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">&lt;/cr:rule-set&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则集的扩展名为</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">.rls</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，由以下几部分构成：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">标签</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> &lt;cr:rule-set&gt; </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">包含了对文档（规则集）中所使用的模式的所有引用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">一个或多个规则，由标签</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> &lt;cr:rule&gt; </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">定义</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">在每个规则里，用标签</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> &lt;cr:condition&gt;</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">定义一个或多个测试条件，用标签</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> &lt;cr:actions&gt;</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">定义一个或多个动作</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">测试条件是输入对象可以满足的测试；操作</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">(action)</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">一般是当规则条件被满足，规则被触发时，被实例化进内存中的对象。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">实际上，建立规则集的最简单方法就是从一个现有的规则集开始，并修改现有的规则集。本文的例子里有几个规则集。可以使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">XML Spy</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">这样的工具手动建立规则集。规则和表达模式在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">p13n_ejb.jar</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件里可以找到，位置是在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> lib/schema</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">目录下。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">部署规则集</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">每个应用程序都与自己的数据实例相关联，这些数据的存储于应用程序的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> META-INF/data </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件夹中。这个文件夹下面以及该文夹件的子文件夹里的任何数据，都能通过数据同步机制进行部署。作为最佳实践，我喜欢在保存规则集的文件夹下面建立一个子文件夹“</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">rulesets</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">”。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则服务使用门户数据同步机制（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal Data Sync</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">）来把规则集部署到运行服务器。也就是说，用户只需把规则集保存在数据同步文件夹里，规则集就会自动被部署。如果文件夹里已经存在相同的规则集，那么数据同步机制会自动检查规则集的变化，规则服务数据库会自动刷新规则集的实例。规则集文件必须以</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">.rls</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">为扩展名，这样规则管理器才会认识它。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> <BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">例如，如果我的应用程序是</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> drtApp</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，我把我的规则集放在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">drtApp/META-INF/data/rulesets</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件夹里。现在，当服务器首次启动，规则服务第一次调用时，这个文件夹里的所有规则集都会被数据同步机制加载，供规则引擎使用。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">那么，如果想在规则控件或</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">EJB API</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">里调用规则集的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">URI</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，那么所使用的文件名应当相对于</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> META-INF/data</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件夹。例如，如果我把规则集</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> simple.rls </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">放在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> drtApp/META-INF/data/rulesets</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件夹里，就应当用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">“</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">/rulesets/simple.rls</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">”这样的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">URI</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">来引用它。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">只要修改数据同步规则集文件夹的内容，数据同步机制就会重新载入规则集（规则集发生修改）或者从规则服务器删除规则集（规则集被删除）。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">这意味着我们可以在服务器运行的时候调整规则，或者对解析错误进行调试。修改规则集之后，无需重新启动服务器。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">规则服务组件</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR>Portal </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控件是易于使用的用于表示和执行业务逻辑的组件。这些组件都是图形控件，可以在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">WebLogic Workshop</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">里使用。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">就是这样一个控件，它可以执行已经命名的规则和规则集，在规则集计算之后还可以对计算结果进行过滤。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">在使用这个控件之前，需要配置控件的几个属性：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">包含规则的规则集的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> URI</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。如前所述，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">URI</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">要相对于应用程序的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">META-INF/data </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件夹。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">用于计算的规则的名称。如果没有指定规则，那么规则集中的全部规则都会被计算。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">可选的过滤器参数，详见下文。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">dev2dev</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">上还有另一篇有关规则的文章，说明了如何</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><A href="http://dev2dev.bea.com.cn/mcmullen.jsp"><SPAN style="FONT-FAMILY: 宋体">使用这个控件动态引导页面流</SPAN></A></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">这个</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控件实际上封装了</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesManager EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">p13n_ejb.jar </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">文件中的任何</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">应用程序都可以使用这个</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，并可用于直接调用规则服务</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">(</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">不过，最好是用控件进行调用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">)</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。有关</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesManager EJB API</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的调用说明，详见</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的在线文档</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">javadocs</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">对规则进行计算</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对规则集进行计算，可以考虑通过以下步骤：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的规则引擎初始化，建立“内存”。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则控件指明要使用哪个规则集，并且还可以进一步指定要对哪个规则进行计算</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> (</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">默认是全部规则都计算</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">)</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，以及是否过滤计算结果。所有这些参数都可以通过</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rules</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控件进行配置，并传递给规则引擎，用于对规则进行计算。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">开发人员建立对象，并把对象加入“内存”。示例对象可能是用户的档案、</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Request</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象等。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">这些参数会作为变量传递给规则控件的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">evaluate() </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">方法</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">通过</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">调用规则引擎。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则引擎根据输入的规则集和输入的对象创建</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Rete Network</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则引擎反复触发，根据输入对象的状态和规则条件执行规则。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">当规则引擎遇到没有规则可以触发的时候，计算结果和所有的原始输入对象一起存在于内存里。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Symbol">·<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">由于输入对象也是结果的一部分，因此可以根据一个类对结果进行过滤。例如，可以指定只返回类</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> com.bea.p13n.usermgmt.profile.ProfileWrapper</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的结果。下面一节的例子显示了如何建立和使用过滤器。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">过滤规则</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">假如现在已经有了一个规则，就像前面例子里的简单规则一样。应用程序的目标是输入用户的档案，检查用户的某些属性，根据这些属性来触发一些规则，如果规则的计算结果为真，则初始化一个新的</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Integer</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象。然后在页面流（</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">page flow</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">））里，可以使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Integer</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">值进行决策。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">在规则引擎已经触发完所有规则并完成所有预定工作结束之后，会返回一个</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> Iterator</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象，它描述了仍然保留在内存里的所有对象。可能没有必要关注内存里有哪些对象，例如用户档案、</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Request</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象等，真正想从结果中得到的，可能只是</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Integer</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象。所以我们需要对结果进行过滤。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">对象过滤器是一个参数，可以在计算规则时传递给规则。如果使用规则控件，参数在控件的属性里指定。如果直接使用控制管理器</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> EJB </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，参数在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> evaluate() </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">方法的参数中指定。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">可以用任何</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Java</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">类或类集来构造</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> ObjectFilter</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，在本例里，我们直接使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesManager EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，像下面这样建立过滤器：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> </SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal vAlign=top>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String rule = "myRule.rls";</SPAN></B><B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN></B><B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class clazz = java.lang.Integer.class;</SPAN></B><B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN></B><B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectFilter filter = new RuleResultClassFilter(rule, clazz); </SPAN></B><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">有关</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"> com.bea.p13n.rules.manager.RuleResultClassFilter </SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">的详细介绍，请参考</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">javadoc</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">。</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">如果正在使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">，那么是在该控件的属性里指定要过滤的类，如下所示：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal vAlign=top bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">/** </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @common:control </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>* @jc:rules-executor filterClassName="java.lang.Integer" filterResults="true"&nbsp; rulesetUri="/rulesets/myRule.rls" </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>*/ </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>private com.bea.p13n.controls.rules.RulesExecutorControl rulesExecutorControl;</P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">使用计算结果</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">现在，假设你已经建立了上面的简单规则，用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">计算了规则，而且还过滤了计算结果。下面是在一个代码片段里它实际的使用情况：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal vAlign=top bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">import com.bea.p13n.usermgmt.profile.ProfileWrapper;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">import com.bea.p13n.controls.*;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">public class Controller extends PageFlowController</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">{</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>/**</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @common:control</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>*/</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>private UserProfileControl profileControl;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">/** </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp; &nbsp;* @common:control </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp; * @jc:rules-executor filterClassName="java.lang.Integer" filterResults="true"&nbsp; rulesetUri="/rulesets/myRule.rls" </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&nbsp;&nbsp; */ </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp; </SPAN>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;private com.bea.p13n.controls.rules.RulesExecutorControl </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>--- other Actions go here --- </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>/**</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @jpf:action</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @jpf:forward name="success" path="success.jsp"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @jpf:forward name="error" path="error.jsp"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>*/</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>protected Forward ruleAction()</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">{</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">try</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">{</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// First, get stuff into the working memory.&nbsp; Let's get the user's city</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ProfileWrapper pw = profileControl.getProfileForUser("cindymc");</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String city = pw.getProperty("Addresses", "City");</P>
<P style="TEXT-ALIGN: left" align=left>// Get the results.&nbsp; We already filtered on Integer for the results, so only Integer will </P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// be in the results.&nbsp; Should be only one.</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>while (iter.hasNext())</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Integer score = (Integer)iter.next();</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>if (score.intValue() &gt; 100)</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>pw.setProperty("Scores", score);&nbsp;&nbsp;&nbsp; // or whatever</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">}</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">catch (Exception e)</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">{</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>return new Forward("error");</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">}</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">return new Forward("success");</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">}</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">}</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><B><SPAN style="COLOR: black; FONT-FAMILY: 宋体">结束语</SPAN></B><B><SPAN style="COLOR: black; FONT-FAMILY: Verdana"></SPAN></B></P>
<P><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana"><BR>Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">规则服务为</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">应用程序提供了访问实时商业智能的方法。可以通过</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesManager EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">直接调用规则服务，也可以使用</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">调用。在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">中，通过</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">EJB</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">调用规则服务已经可用了，但是对</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">RulesExecutorControl</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">控件的使用需要等到</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">Portal SP3</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">发布，其才会得到完全的支持。不过，虽然在</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">SP3</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">之前不支持规则控件，但是你可以从</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Verdana">dev2dev</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体">上直接得到它。</SPAN><SPAN>&nbsp; </SPAN></P><img src ="http://www.blogjava.net/kapok/aggbug/5613.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-06 15:48 <a href="http://www.blogjava.net/kapok/archive/2005/06/06/5613.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>、关于SSO和Weblogic Portal </title><link>http://www.blogjava.net/kapok/archive/2005/06/06/5607.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 06 Jun 2005 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/06/5607.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/5607.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/06/5607.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/5607.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/5607.html</trackback:ping><description><![CDATA[8、关于SSO和Weblogic Portal <BR>本文讨论的只是基于Web的其他应用系统与的Weblogic Portal的SSO。 一、应用系统的验证 提到SSO，就不能不提到其他基于Web应用系统的身份验证。我们透过现象看本质，看看基于Web的身份验证的实质是什么。 无论后端是什么系统，例如Lotus Domino，BO等等，其实，基于Web的身份验证的实质无外乎就是有一个表单（FORM），表单里面让用户输入用户名称和密码，然后提交给验证的页面（Form中的action指定的），通过身份验证后，通过Session来储存用户的一些信息，然后每次访问页面时，从session里面读这些信息，如果存在，则进入登录后的界面，否则，就认为没有登录。 而Session的机制呢，我想大多数人都应该知道，它是与Domain还有Path相关的，是存储在服务器端的，服务器如何知道当前浏览器或者客户端对应的是哪个Session呢，主要是通过Cookie中的sessionid来对应的，session是有失效时间的，服务器一般都可以设置，也可以通过程序来设置。 Cookie的生命周期可以是一关闭浏览器就灭亡，也可以设置存在的时间，如果设置了存在的时间，就会以文件的方式存在于客户端，当客户端再访问服务器时，可以通过Domain对应关系从Cookie中读取相应的信息，一般我们在其他网站时，登录时如果设置了自动登录或者记录多长时间，就是使用的Cookie，一般会在Cookie中存储登录的用户名称和密码的密文，访问网站时，它如果读取到这些信息，就会使用这些信息自动登录了。 所以可以看出，无论对于什么Web应用程序，如果该浏览器曾经登录过，服务器端生成了session，客户端Cookie中记录了SessionID，该浏览器没有关闭，那么，即便该浏览器访问其他的站点后，再回到该应用系统，如果Session没有失效，那么应用程序还会认为你是登录的。 而对于身份验证的页面，一般是不做判断，判断你的用户名称和密码是通过POST，还是URL中参数方式提交的。除此以外，有的应用程序会从URL中参数判断你是否登录，这种方式由于不安全目前已经应用不多，早些年很多门户网站的邮件就是这种机制，用户登录时会根据一些特性生成一个很长的字符串，通过该字符串来判断是否登录了，所以当时会发<BR>生，如果在另外一台机器上拷贝了该URL，也会进入邮箱的情况。 还有一种特殊情况，这是HTTP协议支持的，就是BASIC认证方式，它会在用户初次访问时弹出对话框，用户输入用户名和密码后，浏览器会记住，然后每次HTTP请求时，在Header里面将用户名称和密码的BASE64位形式传给服务器，服务器就会知道该用户的身份了，之所以在这里单独提出这种方式，是因为下文介绍的自己编写的SSO实现很难对付这种验证方式，所以，如果后端系统（例如Lotus Domino）同时也支持Form身份验证，就需要改过来，如果不支持或者不能改过来，将是很棘手的一件事情。 二、SSO的实现-第三方产品 本节讨论的是基于第三方产品实现SSO。 目前，有很多产品支持SSO，使用Weblogic Portal，我们也主张用户使用这些产品，因为他们能够很好的通过配置，甚至是自学习的方式实现SSO，开发者需要完成的工作很少。 这些产品，一般实现的方式有两类：第一类是通过Agent的方式，即在后端每个Web应用系统，或者其他系统都安装一个Agent，由Agent来接管该系统的身份验证和访问控制，同时，需要有一台策略服务器，里面会放置Weblogic Portal的用户信息，以及这些用户与其他系统的用户对应信息，当然，Weblogic Portal也可以继续保留自己的用户，该策略服务器只是存放了用户的对应关系。这类产品对于不同的系统，Agent不同，这些Agent能够通过配置，轻松的接管了后面的系统的身份验证和访问控制，所以，举例来说，如果Portal中用户A对应系统1中的用户B，那么策略服务器中有此配置后，当从Portal访问系统1时，系统1的agent能够辨别portal用户A，就可以知道该系统对应的用户是B，让系统认为当前用户就是B，然后使用B的身份来访问和操作系统1。这类产品的使用方式还可以是，通过一个统一的LDAP，存放企业内部的用户信息，然后通过策略服务器，控制了后端所有系统的URL访问权限，这样也实现了单点登录。 这种方式的优点是：策略服务器不会存储其他系统的密码，密码还是保存在各个系统中，同时，各个系统的访问都由Agent控制，用户必须经过Portal作为入口，同时，可以通过策略服务器灵活的配置访问控制。缺点在于：需要在各个系统安装Agent；对于没有Agent的系统，需要通过安装Web Agent，然后进行一定的编码实现；Portal作为单一入口，一旦当机，无法访问后台系统。缺点解决：Weblogic Portal可以通过构建集群实现负载均衡和容错，避免单点故障。 第二类是通过Proxy的方式，即具有一个Proxy Server，由它来接管对于后端系统的访问，提交请求和读取数据，然后再返回给Portal，同时也有一个LDAP服务器或者策略服务器，该服务器可以存放用户信息以及用户的对应关系。Proxy Server的作用是接收Portal的请求，提交给后端系统，然后将返回的数据再写给Portal，Proxy Server会通过存储的用户对应关系和用户名和密码，自动完成后端系统的登录，然后就象一个浏览器一样，提取数据，返回数据给后端系统。 该方法的优点是：后端系统不用做任何改动。即便是没有Portal，其他系统还可以照常使用。缺点是：需要在策略服务器中存储用户名称和密码，密码会多处存放，同步困难；用户可以绕开Portal，直接访问后端系统。Proxy Server可能是单点故障。 缺点解决：目前有密码同步产品；Proxy Server也大多支持集群。 由以上两种方式可以看出，哪种方式的编程量都不是很大，大多可以通过配置来实现，而且功能也很强大，例如第一节说的BASIC登录方式，这些产品都支持。而Weblogic Portal通过其支持多身份验证提供者，以及良好的开发框架等的特点，能够完全支持这两种方式。<BR>如果客户银子大把，优先应该考虑使用第三方产品。 可是如果客户预算不大，后端系统又不多，有什么解决方法呢？答案当然是有，但不是万能的。 三、Weblogic Portal的用户 提到SSO，就不能不说说Weblogic Portal的用户信息。作为一个统一，简单，可扩展的企业级应用平台Weblogic Platform中的一部分，Weblogic Portal被容纳在Weblogic Platform统一的安全框架中，它使用的用户和组，就是weblogic Server的用户和组，但是与Weblogic Server不同的是，它的角色是Portal特有的，与Server是完全不同意义的，需要注意不要混淆了。 Weblogic Server安装后缺省时是使用自带的内嵌的LDAP来进行用户，组和角色的管理的，在身份验证提供者中，有一个DefaultAuthenticator，就是对这部分用户，组和角色来进行管理的提供者。 Weblogic Portal8.1.3可以支持多个用户身份验证提供者，配置是在Server的控制台中进行，在Weblogic Portal的管理工具中，在管理用户和组的时候，可以在多个安全提供者之间切换，进行管理。 在Weblogic Server控制台中，点击Security &gt; Realms &gt; myrealm&gt; Authentication Providers，可以看到DefaultAuthenticator，同时，还能看到可以新建很多种类的身份验证提供者，包括： Configure a new Default Identity Asserter... Configure a new MedRec Sample Authenticator... Configure a new Open LDAPAuthenticator... Configure a new Novell Authenticator... Configure a new iPlanet Authenticator... Configure a new RDBMSAuthenticator... Configure a new Default Authenticator... Configure a new Realm Adapter Authenticator... Configure a new WSRPIdentity Asserter... Configure a new LDAPX509Identity Asserter... Configure a new Active Directory Authenticator... 可以看到，Weblogic Server可以配置使用多种主流的LDAP服务器来存储用户和组，同时，也支持数据库和AD。通过设置，可以指定使用哪个身份验证提供者作为缺省的。也可以设置多个Provider直接的验证关系如何。 本文中不会对如何配置进行说明，感兴趣的朋友，可以查阅Weblogic Server关于Security的相关的帮助。使用数据库作为验证，可以参见笔者另外一个帖子： <A href="http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=101&amp;threadID=18563">http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=101&amp;threadID=18563</A> 由此可以看到，如果客户需要的SSO指的就仅仅是，能够使用他们企业内部已有的LDAP或者AD用户，进行Weblogic Portal登录和身份验证的话，那么，只要配置多个身份验证提供者就可以了，就可以使用那些已经存在的用户。但是，如果客户需要的SSO并不局限于此，还需要在Portal上登录以后，再访问其他一些基于Web的应用系统时，就不需要重复输入用户名和密码和重复登录了，那么，仅仅通过配置是无法实现的。即便那些系统和Portal理想状态下都使用相同的LDAP或者AD用户，由于每个系统验证用户是否登录以<BR>后的机制不同，还是需要进行定制开发的。 四、自己开发实现SSO Portal作为统一的入口，将其他基于Web的应用集成到Portal中，方式可以是多种多样的，最简单的是Portal上提供链接，直接打开其他系统的界面，进行操作；再复杂一点的就是通过Frame或者Iframe的方法，将其他系统的界面嵌入到Portal中，但实质还是使用其他系统的界面，Portal只是从大范围（例如包含了其他应用的Portlet）来控制用户的访问权限，这两种主要解决的就是能够绕过其他系统的登录，然后让其他系统能够识别当前用户的对应身份，至于其他系统内部自己的个性化，权限等，还是由各个系统自己控制的。Weblogic Portal的Web应用集成还有其他方式，例如通过web clipping的方式，生成Portlet；或者通过HttpControl，取得Http回应的内容，组成Portlet；或者完全使用后端系统的API，重构Web内容，例如通过Lotus Domino的API，访问Lotus Domino的数据库，直接读取视图或者文档的信息等。但这几种方式都已经不再使用原来的系统界面，所以，涉及的内容在本文SSO讨论中没有包括。本节讨论的就是开始时提到的两种方式如何解决。 经过前面身份验证，Session，Cookie等方面的说明，我想，很多人大概已经知道如何自己编写程序来实现简单的SSO了，在论坛上笔者也看到有的朋友这样做了，其实说来很简单： 1、在数据库或者LDAP中存储Portal用户和其他系统用户的对应关系，包括其他系统用户名称和密码，根据不同系统的验证特点，有的可能还要存储密码的密文形式。 2、在Portal登录时，或者在切换到其他系统时，通过Iframe将用户名称和密码通过URL传递过去，进行后端的登录。 以后端系统为Dev2dev.bea.com.cn为例，可以通过查看登录页面的源文件，知道Form的action是login.jspa，那么，当Portal用户验证正确以后，可以在页面中加入 &lt;iframe width=1 height=1 src='http://dev2dev.bea.com.cn/bbs/login.jspa?username=YOURUSERNAME&amp;password=YOURPASSWORD'&gt;&lt;/iframe&gt; &lt;a href="<A href='http://dev2dev.bea.com.cn/bbs/settings!default.jspa">Dev2dev.bea.com.cn</a'>http://dev2dev.bea.com.cn/bbs/settings!default.jspa"&gt;Dev2dev.bea.com.cn&lt;/a</A>&gt; 将上面的YOURUSERNAME和YOURPASSWORD替换为Portal用户对应的用户名称和密码，打开页面后，点击该链接，可以看到，当前用户已经是登录以后的身份。 当然，可以去掉下面的连接，直接设置iframe的width和height为足够大，就可以将dev2dev.bea.com包括在其中。 以上应该是一个JSP页面（因为你要动态的设置名称和密码），通过该JSP页面产生Portlet，就可以嵌入到Portal中，正如我们前面所说，你不能通过Portal控制你登录以后的样式，个性化等属性，但是你可以通过控制用户访问该Portlet的权限，从而实现Portal内高层次的个性化和显示控制。（其实，通过JavaScript，也完全可以改变Iframe中的内容和样式，这个超出本文的主题，略过） 以上就是一个SSO的自己编程的简单实现，但是，这种方法具有很多需要注意的地方和局限性： 1、对方的登录表单，可能不仅仅是传递了用户名称和密码，可能还有其他的参数，需要多次尝试，如果能够看到对方验证的代码最好。 2、对方有可能进行了Form是POST还是GET提交的判断（例如，验证页面是Servlet），如果一定需要使POST，那么可以使iframe中src是一个相同的form表单，action指向对方的验证页面，然后，通过Javascript，进行隐式提交；更有甚者，有的验证页面还判断了是否是本服务器提交的请求，那么，就要嵌入对方的登录表单，然后在iframe所在的页面内，<BR>通过Javascript使iframe内的页面提交，完成登录。 3、有的系统为了安全，密码传输前已经在客户端通过Javascript进行了加密，注意检查。 4、有的系统很特别，一定要在浏览器的_top窗口中进行验证，或者验证以后在_top中打开后继的页面，对于这种系统，请修改对方的程序，否则很难解决。笔者曾经就遇见过此类案例，尝试多次无果，幸好后来发现对方系统能够设置后继的页面，将后继的页面设置为Portal，再转回Portal才可以。 5、Portal用户会和多个系统的用户有对应关系，需要设计一个好的数据结构，如果后端系统为多个，不一定非要在Portal登录验证后隐式登录所有的系统，可以在需要显示哪个系统时，再隐式登录。 五、小结 以上就是个人关于SSO和Weblogic Portal的实现的一些看法，经验和心得体会，希望能对相关朋友有所帮助。值得一提的是，Weblogic Portal通过Portlet源的多样性和灵活性，为自己开发编程实现SSO，提供了强有力的支持。<img src ="http://www.blogjava.net/kapok/aggbug/5607.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-06 15:18 <a href="http://www.blogjava.net/kapok/archive/2005/06/06/5607.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLogic Portal7.0 利用统一用户档案(UUP) --- 下注在技术更替的关键时刻 </title><link>http://www.blogjava.net/kapok/archive/2005/06/06/5604.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 06 Jun 2005 06:56:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/06/5604.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/5604.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/06/5604.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/5604.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/5604.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/wlportal/20030625.html">http://dev2dev.bea.com.cn/techdoc/wlportal/20030625.html</A><BR>本文将会告诉你如何在BEA WebLogic Portal中使用统一用户配置文件（Unified User Profile）。本文通过一些必要的编程以及配置步骤的介绍来告诉读者如何利用门户的内置规则引擎结合外部的用户档案数据来提供一个个性化的门户应用。 
<P><BR>BEA WebLogic Portal运行在WebLogic Server之上，并且拥有一系列的特性和服务，从而使你能够建立一个交互式的、自适应的门户网站。它的一个突出优点是提供了一些能够基于用户档案文件来配置网站的工具。例如，仅当用户档案文件符合特定要求时，你才能够获得授权，从而进入特定的门户页面，甚至是portlets，或者，能够修改门户页面提供的内容或基于用户档案文件的portlet。例如，一个用户可能需要成为某个部门的成员或是在公司中占据特定的职位才能够进入不同的内容条目或是portlet。这可以利用在BEA EBCC（E-Business Control Center）中那些以交互方式定义的的规则来实现。使用这个工具，可以很容易地把工作内容或是市场活动瞄准到特定的用户，并且不需要高深的编程知识。另一方面，作为软件开发人员，你可以享受到如下的好处：利用方便的Java API和Taglib就能够访问并且修改用户档案文件。</P>
<P>不幸的是，数据很少被仅仅存储在企业的一个地方，而且也很少被保存为BEA WebLogic Portal所需要的格式。</P>
<P>实际上，用户档案数据可能被分散地存储在很多不同的系统当中。通常，数据的一部分被存储在像SAP这样的ERP系统中、像Siebel这样的CRM系统中、LDAP目录服务中、像Oracle或 Sybase这样的关系数据库中，以及各种各样其他的地方。幸运的是，BEA Portal没有仅在它的本地数据库模式中提供用户描述的存储。它定义了名为统一用户档案文件（Unified User Profile ，UUP）的机制，使你可以创建所有的与该门户应用(portal application)紧密结合用户档案文件的应用模块。接着，你就能创建内容选取规则(content selection rule)，或是用户权限，从而利用那些已经存在并且安全地存储于企业遗留或是核心系统中的数据。</P>
<P><B><BR>创建统一用户档案</B><BR>本文将会告诉你如何建立并使用一个依赖于外部数据的UUP。我们假设现在有一个大型跨国公司的员工门户。简单地说，我们只考虑一个门户页面，它包括了一个展示公司新闻的portlet和一个展示公司国际分支机构销售数据的portlet。后者仅仅对于销售部门的员工是可见的。新闻portlet根据员工所在的部门、员工的国籍、以及员工在公司内职位的不同来显示公司新闻。有些新闻将会仅仅和软件开发人员相关，因此仅仅对于他们可见，另外一些可能仅仅对于管理人员是可见的。当然，任何信息都可以按照员工首选的语言来显示。<BR><BR>公司已经在不同的数据库中存储了关于员工的大多数数据。值得庆幸的是，该公司实施了面向服务的软件体系结构，所以所有的相关员工数据都能够以像清单1中定义的SOAP Web service的方式被访问。（清单1-3<A href="http://www.sys-con.com/weblogic/sourcec.cfm" target=_blank>附后</A>） <BR><BR>为了使用来自门户的Web服务中的数据，必须首先使用EBCC定义用户档案文件和相关规则。用户档案文件在"site infrastructure"选项卡中建立。按照通常的经验，你应该为每一个要访问的数据源创建至少一个用户档案文件。为了满足要求，你会想要定义两个用户档案文件。第一个是corporateProfile，如图1所示。这个配置文件稍后将会被映射成从Web服务中获得的数据。如果你检查了Web服务的WSDL，将会注意到它没有定义员工的首选语言。因此，你需要创建另外一个名为preferencesData的配置文件，它将被保存在默认的门户数据库模式中。</P>
<P><BR>接下来，你需要建立一些规则(rules)，稍候它们将被应用到用户档案文件上。首先，定义为用户选取特定新闻内容的规则。你需要在EBCC的presentation选项卡中定义一个新的内容选择器。现在，可以定义内容选取规则了。</P>
<P><BR>在定义这些规则时，EBCC会连接到你的门户应用，从而获取可用的内容类型及其元数据。当然，可否获取元数据取决于那些被载入到你门户模式中的内容。出于演示的目的，BEA提供了一个叫做bulk loader的工具。可以使用该工具从具有特殊注解的HTML文件中索引和装载内容。一旦检索出了元数据，你就能够借助可利用的菜单创建一个内容搜索。</P>
<P>图2所示的是组成内容搜索的规则。它只选取了那些符合国家、部门、职务、和用户喜好语言条件的内容条目。把规则用"ShowNews"这个名字进行保存，以便以后在新闻portlet中访问它。使用相似的方法，可以创建一个权限片断，其中包含了所有在销售部门工作的用户，我们把它叫做MemberOfSales。在保持同步的同时，这些规则已经可以在门户应用中使用了。</P>
<P><B><BR>开发UUP</B><BR>现在开始真正的软件开发工作。针对那些可以获得预期结果的规则，你需要提供能够在Web 服务和门户应用之间连接用户档案文件数据的组件。访问用户档案文件的一般结构如图3所示。在BEA WebLogic Portal中，所有对用户档案文件的访问都是通过一个名为UserManager的无状态会话bean实现的。</P>
<P><BR>根据用户档案文件的类型，它授权了对适当ProfileManager EJB的访问。接着，ProfileManager EJB根据被请求的用户档案文件，授权对一个com.bea.p13n.property.EntityPropertyManager类型的无状态会话bean 的访问。这就是实际的用户档案文件访问过程（参见图3）。框架中展示了两个钩子（hook），你可以在这里插入你自己的配置文件访问。只在很少的情况下，你需要为特定的配置文件类型编写一个自定义的ProfileManager。如果你需要提供一个和默认实现功能完全不同的实现，这可以作为你的一个选择。大多数情况下，你希望提供一个自定义的EntityPropertyManager，并且为一个定义在EBCC中的，特定的用户档案文件而注册它。本文余下的部分将会专注于后面这种情况。<BR><BR><IMG height=338 src="http://dev2dev.bea.com.cn/images/article/portal030613/image003.jpg" width=400 border=0 v:shapes="_x0000_i1026"><BR><BR><IMG height=251 src="http://dev2dev.bea.com.cn/images/article/portal030613/image005.jpg" width=400 border=0 v:shapes="_x0000_i1027"><BR><BR><IMG height=162 src="http://dev2dev.bea.com.cn/images/article/portal030613/image007.jpg" width=400 border=0 v:shapes="_x0000_i1028"><BR><BR><IMG height=289 src="http://dev2dev.bea.com.cn/images/article/portal030613/image009.jpg" width=400 border=0 v:shapes="_x0000_i1029"><BR><B><BR>EBCC中定义的用户档案文件</B><BR>你需要定义一个其远程接口为EntityPropertyManage类型的EJB，并且把它部署在BEA WebLogic Portal所在的EAR中，同时，把它注册到门户的用户管理工具中去。清单3展示了组成CorporateProfileManager这个EJB的所有的类。在我们的例子中，用户数据是只读的，不能被门户的配置文件管理工具所修改。在企业环境中这并不少见，这样的数据可能只有授权用户才能够编辑。 
<P><BR>在这样的环境中，必须以非空的形式实现的EntityPropertyManager的方法是：</P>
<P>public Object getProperty(PropertyLocator propertyLocator,<BR>String pSet, String pName) throws EntityNotFoundException;</P>
<P>public EntityPropertyCache getProperties(PropertyLocator propertyLocator)<BR>throws EntityNotFoundException; </P>
<P>对于其他方法，你应该抛出一个如上面代码中所示的UnsupportedOperation异常。值的注意的是，把用户档案文件的检索结果缓冲一定的时间是一种很好的做法。同样，getProperty()方法通常把任务委托给getProperties() 方法。该方法一次性地为一个特定用户检索出用户档案数据，并且缓冲它们以便后面使用。为用户档案文件提供数据的Web服务由BEA WebLogic Workshop来创建。CorporateProfileManager使用由BEA WebLogic Workshop创建的stubs来访问隐藏了所有实现细节的服务。</P>
<P>EJB被打包在自己的jar文件中，并且被部署在门户所在的企业应用中。最后，你需要通知ProfileManager EJB：它应该委托新部署的CorporateProfileManager EJB，为所有向用户档案文件"corporateProfile"发出的请求提供服务，这可以通过在ProfileManager的配置描述文件中把用户档案文件映射到EJB来实现。ProfileManager位于usermgt.jar中。 </P>
<P>你可以使用WebLogic控制台来编辑配置描述文件，或者采用从jar中提取文件，修改它们，然后重新打包成jar的方法。如同下面展示的那样，你需要添加一个新的封闭条目来映射CorporateProfileManager的配置文件。条目的名称必须符合PropertyMapping/&lt;profileName&gt;的格式，其中&lt;profileName&gt;是指EBCC中定义的用户档案文件的名字。你还需要针对刚刚建立的CorporateProfileManager ejb创建一个ejb-ref（ejb描述文件）（参见清单4）。</P>
<P><BR>最后，你需要编辑weblogic-ejb-jar.xml文件，把在web.xml中建立的ejb-ref-name映射为如下所示的已部署的EJB的实际JNDI名字。</P>
<P>&lt;weblogic-enterprise-bean&gt;<BR>&lt;ejb-name&gt;UserProfileManager&lt;/ejb-name&gt;<BR>&lt;reference-descriptor&gt;<BR>&lt;ejb-reference-description&gt;<BR>&lt;ejb-ref-name&gt;ejb/CorporateProfileManager&lt;/ejb-ref-name&gt;<BR>&lt;jndiname&gt;<BR>.BEA_personalization.CorporateProfileManager<BR>&lt;/jndi-name&gt;<BR>&lt;/ejb-reference-description&gt;<BR>&lt;/reference-descriptor&gt;<BR>&lt;/weblogic-enterprise-bean&gt; </P>
<P>当应用了这些修改之后，你需要做的仅仅是重新部署企业应用。现在你的用户档案文件已经可以使用了。 </P>
<P><BR><B>运行状态的UUP</B><BR>现在，我们来看看新的用户档案文件如何运行。假设有两个员工：Sima是在德国法兰克福工作的开发部经理；Teresa是在英国伦敦工作的销售部职员。</P>
<P>Sima的首选语言是德语，Teresa的首选语言却是英语。</P>
<P>为了测试配置文件是否按照你所要求的那样被访问，你可以使用WebLogic Portal的management WebApp。选择user management-&gt;users，然后选择一个用户。为这个用户选择corporateProfile作为察看属性。如果一切工作正常，你应该看到从Web服务中检索出来的实际的数值。（参考图4）现在，使用EBCC portlet创建向导创建并且部署你自己的portlets。利用在EBCC中定义好的内容选择器来选择要显示给用户的合适的新闻内容。清单3显示了一个简单的新闻portlet。实际执行内容选择器的代码如下所示：</P>
<P>&lt;pz:contentSelector rule="ShowNews"<BR>contentHome="&lt;%=ContentHelper.DEF_DOCUMENT_MANAGER_HOME %&gt;"<BR>id="news"/&gt; </P>
<P><BR>需要注意的是我们所使用的实际规则既访问了preferencesData，也访问了corporateProfile，从而获取数据。实际上，我们可以把不同来源的数据混合到一起，从中选择内容或是建立用户访问权限。在portalTools Web application中，你需要使portlet可用并且可见。同样，你需要在销售portlet上应用管理权限片断。（参见图5）<BR></P>
<P><BR><BR>图6a和6b分别展示了用户Teresa和Sima的门户显示。<BR><BR><IMG height=148 src="http://dev2dev.bea.com.cn/images/article/portal030613/image013.jpg" width=400 border=0 v:shapes="_x0000_i1030"><BR><BR><IMG height=148 src="http://dev2dev.bea.com.cn/images/article/portal030613/image015.jpg" width=400 border=0 v:shapes="_x0000_i1031"><BR><BR>正如期望的那样，既然Teresa工作在销售部门，她就看到销售数据的portlet。她的所有新闻条目都是按照她配置文件设定的那样，用英文显示。Sima看不到销售数据，因为她不是销售部门的一员，而且她不具备察看销售portlet的权限。她的新闻条目都是德语的，她还可以看到许多不同的新闻条目，因为她在开发部门工作。</P>
<P><B>小结</B><BR>到目前为止，你已经掌握并且理解了在BEA WebLogic Portal中建立UUP的基本机制。通过部署和注册适当的EntityPropertyManager EJB你可以集成任何数目的遗留数据源，基于此，你能够创建用户档案文件。而且你还能--在你所能做的其他事情中--在你的门户应用中提供个性化的内容和细粒度的访问控制。</P>
<P>当然，在实际的项目中还必须对实际的EJB设计进行其他重要的考虑。例如，你的后端数据源不像你所要求的那样可用并且可靠。在这种情况下，你可能会创建一个本地持久缓冲。这个缓冲可能存在的时间非常短而且可能仅仅作为备用数据，以防真正提供数据的系统掉线。</P>
<P>你还可以使用BEA 缓冲库（BEA caching library）为缓冲EntityPropertyManager中的数据提供更好的控制。<BR>考虑到未来的开发，你还应该关注一下BEA提供的另外一个工具。WebLogic Liquid Data是专门为集成来自不同数据源的数据而设计的。插入到WebLogic Portal之后，WebLogic Liquid Data将会使实施用户档案文件的过程更加容易和迅速。</P>
<P><BR>清单 1: web服务 WSDL</P>
<P>&lt;?xml version="1.0" encoding="utf-8"?&gt;<BR>&lt;!-- -info:link source="UserInfoService.jws" autogen="true" --&gt;</P>
<P>&lt;definitions xmlns="http://schemas.xmlsoap.org/wsdl/"<BR>xmlns:conv="http://www.openuri.org/2002/04/soap/conversation/"<BR>xmlns:cw="http://www.openuri.org/2002/04/wsdl/conversation/"<BR>xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"<BR>xmlns:jms="http://www.openuri.org/2002/04/wsdl/jms/"<BR>xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"<BR>xmlns:s="http://www.w3.org/2001/XMLSchema"<BR>xmlns:s0="http://www.openuri.org/"<BR>xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"<BR>xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"<BR>xmlns:xm="http://www.bea.com/2002/04/xmlmap/"<BR>targetNamespace="http://www.openuri.org/" &gt;<BR>&lt;types&gt;<BR>&lt;s:schema attributeFormDefault="qualified" elementFormDefault="qualified"<BR>targetNamespace="http://www.openuri.org/"&gt;<BR>&lt;s:element name="getUserInfo"&gt;<BR>&lt;s:complexType&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="userId" type="s:string" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;/s:element&gt;<BR>&lt;s:element name="getUserInfoResponse"&gt;<BR>&lt;s:complexType&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="getUserInfoResult" type="s0:UserInfo" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;/s:element&gt;<BR>&lt;s:complexType name="UserInfo"&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="address" type="s0:Address" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="person" type="s0:Person" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="orgaInfo" type="s0:OrgaInfo" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;s:complexType name="Address"&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="street" type="s:string" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="city" type="s:string" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="postcode" type="s:string" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="country" type="s:string" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;s:complexType name="Person"&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="firstNames" type="s:string" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="lastNames" type="s:string" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;s:complexType name="OrgaInfo"&gt;<BR>&lt;s:sequence&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="department" type="s:string" /&gt;<BR>&lt;s:element minOccurs="0" maxOccurs="1" name="function" type="s:string" /&gt;<BR>&lt;/s:sequence&gt;<BR>&lt;/s:complexType&gt;<BR>&lt;s:element nillable="true" name="UserInfo" type="s0:UserInfo" /&gt;<BR>&lt;/s:schema&gt;<BR>&lt;/types&gt;<BR>&lt;message name="getUserInfoSoapIn"&gt;<BR>&lt;part name="parameters" element="s0:getUserInfo" /&gt;<BR>&lt;/message&gt;<BR>&lt;message name="getUserInfoSoapOut"&gt;<BR>&lt;part name="parameters" element="s0:getUserInfoResponse" /&gt;<BR>&lt;/message&gt;<BR>&lt;portType name="UserInfoServiceSoap"&gt;<BR>&lt;operation name="getUserInfo"&gt;<BR>&lt;input message="s0:getUserInfoSoapIn" /&gt;<BR>&lt;output message="s0:getUserInfoSoapOut" /&gt;<BR>&lt;/operation&gt;<BR>&lt;/portType&gt;<BR>&lt;binding name="UserInfoServiceSoap" type="s0:UserInfoServiceSoap"&gt;<BR>&lt;soap:binding transport=<BR>"http://schemas.xmlsoap.org/soap/http" style="document"/&gt;<BR>&lt;operation name="getUserInfo"&gt;<BR>&lt;soap:operation soapAction=<BR>"http://www.openuri.org/getUserInfo" style="document"/&gt;<BR>&lt;input&gt;<BR>&lt;soap:body use="literal" /&gt;<BR>&lt;/input&gt;<BR>&lt;output&gt;<BR>&lt;soap:body use="literal" /&gt;<BR>&lt;/output&gt;<BR>&lt;/operation&gt;<BR>&lt;service name="UserInfoService"&gt;<BR>&lt;port name="UserInfoServiceSoap" binding="s0:UserInfoServiceSoap"&gt;<BR>&lt;soap:address location=<BR>"http://C800-001:7501/userInfoService/UserInfoService.jws"/&gt;<BR>&lt;/port&gt;<BR>&lt;/service&gt;<BR>&lt;/definitions&gt;</P>
<P>清单 2: CorporateProfileManager</P>
<P>/**<BR>* The remote interface of the ejb that will access the corporateProfile<BR>*/<BR>package com.iternum.uup;</P>
<P>public interface CorporateProfileManager extends com.bea.p13n.property.EntityPropertyManager {</P>
<P>}</P>
<P>/**<BR>* Create the implementation of the CorporateProfileManager. This class<BR>* does not support dynamic properties nor any write operations and will<BR>* throw UnsupportedOperationException accordingly<BR>* iternum GmbH (bankkar)<BR>*/<BR>package com.iternum.uup;</P>
<P>import com.bea.p13n.property.*;<BR>import com.bea.p13n.property.internal.PropertyMapKeyImpl;<BR>import com.bea.p13n.property.internal.EntityPropertyCacheImpl;</P>
<P>import weblogic.jws.proxies.*;<BR>import java.rmi.RemoteException;<BR>import java.io.IOException;<BR>import java.util.HashMap;</P>
<P>import org.openuri.www.UserInfo;</P>
<P>import javax.ejb.EJBException;<BR>import javax.ejb.SessionContext;<BR>import javax.ejb.CreateException;</P>
<P>public class CorporateProfileManagerImpl implements javax.ejb.SessionBean {</P>
<P>private HashMap caches = new HashMap();<BR>private SessionContext context;</P>
<P>public CorporateProfileManagerImpl() {<BR>}</P>
<P>/**<BR>*<BR>* propertyLocator<BR>* pSet<BR>* pName<BR>* <BR>* EntityNotFoundException<BR>*/<BR>public Object getProperty(PropertyLocator propertyLocator, String pSet, String pName)<BR>throws EntityNotFoundException {<BR>EntityPropertyCache cache = getProperties(propertyLocator);<BR>return cache.get(new PropertyMapKeyImpl(pSet,pName));<BR>}</P>
<P>/**<BR>* TODO: Use application wide caching<BR>* Returns all properties that are available for a given property locator.<BR>* Caches the properties locally.<BR>* propertyLocator<BR>* <BR>* EntityNotFoundException<BR>*/<BR>public EntityPropertyCache getProperties(PropertyLocator propertyLocator)<BR>throws EntityNotFoundException {<BR>EntityPropertyCache cache = (EntityPropertyCache)caches.get(propertyLocator.getPkString());<BR>if (cache != null) {<BR>return cache;<BR>}<BR>cache = new EntityPropertyCacheImpl();</P>
<P>PropertyMapKey nextKey = null;</P>
<P>String pSet = "corporateProfile";<BR>try {<BR>String userName = propertyLocator.getPkString();<BR>UserInfoService service = new UserInfoService_Impl();<BR>UserInfo info = service.getUserInfoServiceSoap().getUserInfo(userName);<BR>cache.put(new PropertyMapKeyImpl(pSet,"firstnames"),info.getPerson().getFirstNames());<BR>cache.put(new PropertyMapKeyImpl(pSet,"lastname"),info.getPerson().getLastNames());<BR>cache.put(new PropertyMapKeyImpl(pSet,"street"),info.getAddress().getStreet());<BR>cache.put(new PropertyMapKeyImpl(pSet,"postcode"),info.getAddress().getPostcode());<BR>cache.put(new PropertyMapKeyImpl(pSet,"city"),info.getAddress().getCity());<BR>cache.put(new PropertyMapKeyImpl(pSet,"country"),info.getAddress().getCountry());<BR>cache.put(new PropertyMapKeyImpl(pSet,"division"),info.getOrgaInfo().getDepartment());<BR>cache.put(new PropertyMapKeyImpl(pSet,"function"),info.getOrgaInfo().getFunction());<BR>caches.put(propertyLocator.getPkString(),cache);<BR>} catch (IOException e) {<BR>e.printStackTrace(); <BR>//To change body of catch statement use Options | File Templates.<BR>}<BR>return cache;<BR>}</P>
<P><BR>public void removeProperties(PropertyLocator propertyLocator) <BR>throws EntityNotFoundException {<BR>throw new UnsupportedOperationException("Read only profile");<BR>}<BR>public Object removeProperty(PropertyLocator propertyLocator, String s, String s1) <BR>throws EntityNotFoundException {<BR>throw new UnsupportedOperationException("Read only profile");<BR>}</P>
<P>public void setProperty(PropertyLocator propertyLocator, String s, String s1, Object o)<BR>throws PropertyValidationException, EntityNotFoundException {<BR>throw new UnsupportedOperationException("Read only profile");<BR>}</P>
<P>public void removeEntity(PropertyLocator propertyLocator) <BR>throws EntityNotFoundException {<BR>throw new UnsupportedOperationException("Read only profile");<BR>}</P>
<P>public long createUniqueId(String s, String s1) <BR>throws ConfigurableEntityCreateException {<BR>throw new UnsupportedOperationException("Read only profile");<BR>}</P>
<P>/**<BR>* Not supported<BR>* EntityNotFoundException<BR>*/<BR>public String[] getDynamicProperties(PropertyLocator propertyLocator, String s) <BR>throws EntityNotFoundException {<BR>throw new UnsupportedOperationException();<BR>}</P>
<P>/**<BR>* Not supported<BR>* EntityNotFoundException<BR>*/<BR>public long getUniqueId(String s, String s1) throws EntityNotFoundException {<BR>throw new UnsupportedOperationException();<BR>}</P>
<P>/**<BR>* Not supported<BR>* EntityNotFoundException<BR>*/<BR>public PropertyLocator getPropertyLocator(long l) throws EntityNotFoundException {<BR>throw new UnsupportedOperationException();<BR>}</P>
<P>/**<BR>* Not supported<BR>* EntityNotFoundException<BR>*/<BR>public String getHomeName(long l) throws EntityNotFoundException {<BR>throw new UnsupportedOperationException();<BR>}</P>
<P>/**<BR>* Not supported<BR>*/<BR>public String[] getEntityNames(String s) throws RemoteException {<BR>throw new UnsupportedOperationException();<BR>}</P>
<P>public void setSessionContext(SessionContext sessionContext) throws EJBException {<BR>context = sessionContext;<BR>}</P>
<P>public void ejbRemove() throws EJBException {<BR>}</P>
<P>public void ejbActivate() throws EJBException {<BR>}</P>
<P>public void ejbPassivate() throws EJBException {<BR>}</P>
<P>public void ejbCreate() throws CreateException {<BR>}<BR>}</P>
<P>/**<BR>* Home interface for the coporate profile manager.<BR>We just need a default create method here<BR>* iternum GmbH (bankkar)<BR>* : CorporateProfileManagerHome.java,v 1.1 2003/01/15 16:54:17 bankkar Exp 505BR&gt;*/<BR>package com.iternum.uup;</P>
<P>import javax.ejb.CreateException;<BR>import java.rmi.RemoteException;</P>
<P>public interface CorporateProfileManagerHome extends javax.ejb.EJBHome {</P>
<P>public CorporateProfileManager create() throws CreateException, RemoteException;</P>
<P>}</P>
<P>清单 3: news portlet代码段</P>
<P>&lt;%@ page import="com.bea.p13n.content.ContentHelper"%&gt;<BR>&lt;%@ taglib uri="es.tld" prefix="es" %&gt;<BR>&lt;%@ taglib uri="pz.tld" prefix="pz" %&gt;<BR>&lt;%@ taglib uri="um.tld" prefix="um" %&gt; <BR>&lt;%@ taglib uri="cm.tld" prefix="cm" %&gt; </P>
<P>&lt;h3&gt;iternum latest news&lt;/h3&gt;</P>
<P>&lt;pz:contentSelector rule="ShowNews" <BR>contentHome="&lt;%=ContentHelper.DEF_DOCUMENT_MANAGER_HOME %&gt;"<BR>id="news"/&gt;<BR>&lt;es:forEachInArray array="&lt;%=news%&gt;" id="newsItem" <BR>type="com.bea.p13n.content.Content"&gt;<BR>&lt;p&gt;&lt;cm:printDoc id="newsItem"/&gt;&lt;/p&gt;<BR>&lt;/es:forEachInArray&gt;</P>
<P><BR>清单 4: ejb-ref that references the Corporate Profile Manager EJB&lt;session&gt;</P>
<P>&lt;ejb-name&gt;UserProfileManager&lt;/ejb-name&gt;<BR>...<BR>&lt;env-entry&gt;<BR>&lt;env-entry-name&gt;PropertyMapping/corporateProfile&lt;/env-entry-name&gt;<BR>&lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;<BR>&lt;env-entry-value&gt;CorporateProfileManager&lt;/env-entry-value&gt;<BR>&lt;/env-entry&gt;<BR>...<BR>&lt;ejb-ref&gt;<BR>&lt;description&gt;ejb/CorporateProfileManager&lt;/description&gt;<BR>&lt;ejb-ref-name&gt;ejb/CorporateProfileManager&lt;/ejb-ref-name&gt;<BR>&lt;ejb-ref-type&gt;Session&lt;/ejb-ref-type&gt;<BR>&lt;home&gt;com.iternum.uup.CorporateProfileManagerHome&lt;/home&gt;<BR>&lt;remote&gt;com.iternum.uup.CorporateProfileManager&lt;/remote&gt;<BR>&lt;ejb-link&gt;CorporateProfileManager&lt;/ejb-link&gt;<BR>&lt;/ejb-ref&gt;<BR>...<BR>&lt;/session&gt;<BR></P><B>参考文献：</B><BR>　　BEA WebLogic Portal Documentation: Implementing User Profiles: <A href="http://edocs.bea.com/wlp/docs70/dev/usrgrp.htm#998993" target=_blank>http://edocs.bea.com/wlp/docs70/dev/usrgrp.htm#998993</A> <BR>　　BEA WebLogic Portal Documentation: Portal Content Management: <A href="http://edocs.bea.com/wlp/docs70/dev/conmgmt.htm#1018613" target=_blank>http://edocs.bea.com/wlp/docs70/dev/conmgmt.htm#1018613</A> <B><BR><BR>关于作者</B> <BR>Karl F. Banke是位于德国法兰克福的iternum GmbH公司的首席咨询专家和总经理。他的工作领域涉及J2EE和EJB体系结构、Web Services以及项目管理。 <img src ="http://www.blogjava.net/kapok/aggbug/5604.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-06 14:56 <a href="http://www.blogjava.net/kapok/archive/2005/06/06/5604.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache Beehive 相关介绍</title><link>http://www.blogjava.net/kapok/archive/2005/05/20/4977.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 20 May 2005 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/20/4977.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4977.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/20/4977.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4977.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4977.html</trackback:ping><description><![CDATA[<SPAN class=atitle2><A href="http://www-128.ibm.com/developerworks/cn/opensource/os-beehive/">http://www-128.ibm.com/developerworks/cn/opensource/os-beehive/</A><BR><BR>开源 SOA 工具的编程技术</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 初级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/opensource/os-beehive/#author1"><NAME>Kunal Mittal</NAME></A><BR>门户/J2EE 架构设计师和咨询顾问<BR>2005 年 5 月 09 日</P>
<BLOCKQUOTE><ABSTRACT-EXTENDED>Beehive 是一个新的 Apache 项目，用于简化 Java 2 Platform, Enterprise Edition（J2EE）以及 Web 服务编程。本文将介绍如何使用 Beehive，并概要介绍 Pollinate，这是一个用于创建 Beehive 应用程序的 Eclipse 插件。</ABSTRACT-EXTENDED></BLOCKQUOTE>
<P>BEA Systems Inc. 和 Apache 在 2004 年 5 月 25 日宣布了 Beehive 项目的诞生。这个项目着重用于简化 Java 2 Platform, Enterprise Edition（J2EE）和 Web 服务编程，它使用了注释（annotation）的概念，这种技术很快就会成为 Java 技术的一部分。面向服务的架构（SOA）开发也是这个项目的一个重点。</P>
<P>Beehive 是按照 Apache License V2.0 许可协议进行发布的，BEA 对此项目进行了广泛的支持。现在，大约有一半的 Beehive 代码提交者都是 BEA 公司的雇员，这清楚地表明社区中还有其他一些人正在对这个项目贡献着力量。</P>
<P>Apache Beehive 项目创建是为了支持以下功能：</P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>Java Page Flow （JPF）技术，也称为（NetUI） 
<LI>控件 
<LI>基于 Java Specification Request（JSR）181 的 Web 服务</LI></UL>
<P>最终，这些技术将一起汇集到 Model-ViewController（MVC）编程模型中，如图 1 所示。JPF 技术形成控件器层。NetUI 标记库则会参与视图层。模型层是使用 Java 控件构建的。</P>
<P><A name=IDAFVUIB><B>图 1. MVC 模型和 Apache Beehive 技术</B></A><BR><IMG height=278 alt="MVC 模型和 Apache Beehive 技术" src="http://www-128.ibm.com/developerworks/cn/opensource/os-beehive/beehive1.gif" width=565 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="30%" align=right border=1>
<TBODY>
<TR>
<TD background=/developerworks/cn/i/bg-gold.gif>
<P><A name=IDA2UUIB><B>Pollinate 插件</B></A><BR>NetUI、控件和 Web 服务技术一起用于简化 J2EE 和 SOA 的开发。另外还有一个名为 Pollinate 的 Eclipse 插件项目也在进展之中。这个项目可以允许开发人员使用 Eclipse 来构建 Apache Beehive 应用程序。我相信其他 IDE，例如 IntelliJ 和 JBuilder 很快也会开始支持 Apache Beehive。</P></TD></TR></TBODY></TABLE></P>
<P>下面让我们更详细地来了解一下 Apache Beehive 中使用的各种技术。</P>
<P><A name=IDARUUIB><SPAN class=atitle2>NetUI 还是 JPF</SPAN></A><BR>如果您曾经使用过 BEA WebLogic Workshop V8.1，就可能使用过 JPF 技术，它是直接在 Struts 之上构建的。正如您可能了解的一样，Struts 是目前市场中采用最为广泛的一种 MVC 框架。因此，如果 JPF 是在 Struts 之上构建的，那么为什么不使用 Struts 呢？</P>
<P>JPF 充分发挥了 Structs 的关键功能，但却省略了 Struts 所需要的很多繁琐工作（指对部署配置文件的管理，例如 struts-config.xml 文件）。在 BEA 的 Page Flows 最初版本中，引入了一种由 BEA WebLogic Workshop 自动生成和维护的声明性编程语言。与之相反，Apache Beehive 版本的 Page Flows 使用 JSR 175 来定义自己的元数据。JSR 175 是一种元数据规范，它可以让您降低 J2EE 的编码量。</P>
<P>JPF 技术中也提供了一组标签库，名为 <I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">NetUI</I>。在典型的 MVC 设计模式中，JPF 用来构成控制器层。NetUI 标签库可以在表示层中提供帮助。实际上，您可以让一个 Web 应用程序同时使用 Struts 和 JPF。</P>
<P>清单 1 给出了一个简单的 JPF 控制器，它会打印 HelloWorld。</P><A name=IDAEUUIB><B>清单 1. 简单的 HelloWorld JPF 控制器</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>

import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
import org.apache.beehive.netui.pageflow.Forward;

@Jpf.Controller (
    simpleActions= {
       @Jpf.SimpleAction (name="cancel", path="begin.do")
   }
)

public class HelloWorldController extends PageFlowController {


   @Jpf.Action (
       forwards= {
           @Jpf.Forward (name="success", path="helloworld.jsp")
       }
   )
 
   public Forward begin() {
      return new Forward("success");
   }
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意，这里使用了注释。我首先使用 <CODE>@Jpf.Controller</CODE> 注释来定义这个类是一个 JPF 控制器。<CODE>@Jpf.SimpleAction</CODE> 注释定义了这个控制器中的操作。这些注释与 Struts 中操作（action）的概念类似。<CODE>@Jpf.Action</CODE> 注释定义了操作本身。<CODE>@Jpf.Forward</CODE> 注释定义了在流程的下一个阶段会发生什么。这个注释与 Struts 配置文件中信息的概念类似。</P>
<P>前向注释（forward annotation）中引用的 HelloWorld JSP 文件在本文中并没有介绍。对于这个例子来说，它可以是任何 JSP 文件，只要包含一行向屏幕上打印 HelloWorld 信息的代码即可。</P>
<P>NetUI 技术还包括 3 个标签库，它们的基本功能是简化 JSP 文件的开发，并在视图层和控制器层之间提供自动的数据绑定。这些标签都可以支持 JavaScript，因此您可以像标准的 HTML 标签（例如 <CODE>input</CODE> 和 <CODE>select</CODE>）一样使用它们。这些标签库包括：</P>
<DL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DT><B>NetUI</B> 
<DD>这个标签库包括与 struts-html 标签类似的一些标签。 
<DT><B>NetUI-data</B> 
<DD>NetUI-data 标签库用来将表单和控制器中的数据绑定到 JSP 文件中。它可以让您快速显示数据列表，例如搜索结果。 
<DT><B>NetUI-template</B> 
<DD>您可以在 JSP 文件中使用 NetUI-template 标签库来创建子节（或模板）。</DD></DL>
<P>有关 NetUI 标签库的更多信息，请参阅 <A href="http://www-128.ibm.com/developerworks/cn/opensource/os-beehive/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>。</P>
<P><A name=IDAYSUIB><SPAN class=atitle2>控件</SPAN></A><BR>控件是一种非常有趣的技术。控件对基于 SOA 的开发提供了一层很好的抽象和封装。控件是一些封装在 EJB 或消息驱动 bean 中的业务逻辑组件。它们为所有的资源集（数据库、外部系统，等等）提供了一组通用的接口。您可以将控件拖放到 Java 应用程序或 Web 服务中，从而为应用程序或 Web 服务提供这种功能。这可以帮助加速开发过程。</P>
<P>与 Apache Beehive 的其他部分类似，控件也大量使用了 JSR 175 注释。控件通过提供一种允许访问各种类型的资源的统一客户机模型，从而降低了作为一个 J2EE 资源的客户机的复杂性和学习曲线。</P>
<P>清单 2 给出了一个 HelloWorld Java 控件。</P><A name=IDAMSUIB><B>清单 2. HelloWorld Java 控件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.apache.beehive.controls.api.bean.*;
@ControlInterface
public interface HelloWorld {
    String sayHelloWorld ();
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在这个例子中，我定义了一个简单的 ControlInterface。如果您去除 <CODE>@ControlInterface</CODE> 注释，这就是一个简单的 Java 接口类。清单 3 显示了 HelloWorld 控件接口的控件实现。</P><A name=IDA4RUIB><B>清单 3. HelloWorld Java 控件实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import org.apache.beehive.controls.api.bean.*;

@ControlImplementation
public class HelloImpl implements HelloWorld {
    public String sayHelloWorld ()  {
        return "hello!";
    }
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>这个清单中的代码非常简单，不是吗？现在，让我们使用这个例子构建一个 Web 服务。</P>
<P><A name=IDAERUIB><SPAN class=atitle2>使用 JSR 181 的 Web 服务</SPAN></A><BR>JSR 181 是基于 JSR 171 注释标准的 Web 服务所使用的一种元数据标准。Apache Beehive 使用 JSR 181 来定义一组注释，您可以使用它们将任何 Java 类暴露为 Web 服务。Beehive Web 服务开发背后的思想是开发者编写一个简单的 Java 类，然后将其功能暴露为一个 Web 服务。</P>
<P>清单 4 给出了一个简单的 HelloWorld Web 服务。</P><A name=IDAQRUIB><B>清单 4. 简单的 HelloWorld Web 服务</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService(
    targetNamespace="targetNamespace = 
           http://www.openuri.org/my/web/service/wsdl"
)

public class HelloWorld {
    @WebMethod
    public String sayHelloWorld() {        
        return "Hello world!";
    }
}</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在这个清单中，如果您删除黑体表示的元素，那么它就是一个简单的 Java 类。使用 <CODE>@WebService</CODE> 和 <CODE>@WebMethod</CODE> 注释，我们已经将这个简单的 Java 类暴露为一个 Web 服务了。</P>
<P>现在，使用 Apache Beehive 技术，您就可以更好地理解如何开始在您的项目中使用 Apache Beehive 了。</P>
<P><A name=IDA1QUIB><SPAN class=atitle2>使用 Eclipse 来构建 Beehive 应用程序</SPAN></A><BR>Eclipse Pollinate 项目是目前惟一支持 Apache Beehive 开发的 IDE 环境。这种限制随着将来 Apache Beehive 的不断发展而会不断变化。基本上来说，Pollinate 是 Eclipse 的一个可以支持 Beehive 框架的插件。Eclipse IDE 和 Pollinate 与一个 Apache Tomcat 之类的服务器的组合可以构造一个完美的环境，从而简单而快速地开发 Beehive 应用程序。</P>
<P><A name=IDAYQUIB><SPAN class=atitle3>开始使用 Pollinate</SPAN></A><BR>如果您的机器上没有安装 Eclipse，那么请首先下载并安装它。要在 Eclipse 中安装 Pollinate，请启动 Eclipse IDE，然后：</P>
<OL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>选择 Help &gt; Software Updates &gt; Find and Install。 
<LI>在 Install/Update 窗口中，选择 Search for new features to install。 
<LI>在下一个窗口中，单击 New Remote Site。 
<LI>在 New Update Site 窗口中，输入 <CODE>Pollinate</CODE> 和 URL <CODE>http://download.eclipse.org/technology/pollinate/update-site</CODE>。 
<LI>展开树形结构中的 Pollinate，然后选中 Integration Builds 复选框。 
<LI>在下一个窗口中，选择 Eclipse Pollinate Tools 复选框，并完成安装过程。</LI></OL>
<P>成功安装 Pollinate 插件之后，您可以创建一个新的 Beehive 项目。要创建一个这种项目，请在 Eclipse 中选择 File &gt; New &gt; Other，启动 New Wizard，然后选择 Pollinate &gt; Web Application。</P>
<P>当然，您还需要下载并安装 <A href="http://incubator.apache.org/beehive/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Apache Beehive</A>。</P>
<P><A name=IDAPYUIB><SPAN class=atitle2>结束语</SPAN></A><BR>Apache Beehive 项目似乎正在蓬勃发展。BEA Systems 公司做了一个非常聪明的决定，将这部分核心技术在开源社区中公开发布，这样可以促进这些技术的成熟并扩大采用范围。最初，这些技术只在 BEA WebLogic 平台上获得了支持。然而，在开源社区公开发布之后，现在您就应该可以在 IBM® WebSphere® Application Server 以及其他应用服务器上利用这些技术了。您还可以使用基于 Eclipse 的 IBM Rational® 工具（例如 IBM Rational Software Architect 和 IBM Rational Software Modeler）来设计并开发 Beehive 应用程序。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/opensource/library/os-beehive/" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">英文原文</A>。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/library/os-ecov/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Getting started with the Eclipse Platform</A>（developerWorks，2002 年 9 月）介绍了 Eclipse 的历史和概述信息，包括如何安装 Eclipse 及插件的详细步骤。<BR><BR>
<LI>下载并学习有关 <A href="http://incubator.apache.org/beehive/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Apache Beehive</A> 的更多知识。<BR><BR>
<LI>要学习更多有关 NetUI 和 JPF 的知识，请参阅 <A href="http://incubator.apache.org/beehive/pageflow/pageflow_overview.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Page Flow Overview</A>。<BR><BR>
<LI>要阅读更多有关控件的内容，请参阅 <A href="http://incubator.apache.org/beehive/controls/controlsOverview.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Controls Overview</A>。<BR><BR>
<LI>您可以在 <A href="http://www.jcp.org/en/home/index" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java Community Process</A> 上阅读 <A href="http://www.jcp.org/en/jsr/detail?id=181" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JSR 181 Web 服务元数据规范</A>，以及 <A href="http://www.jcp.org/aboutJava/communityprocess/review/jsr175/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JSR 175</A> 规范。<BR><BR>
<LI><A href="http://wiki.apache.org/beehive" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Apache Wiki</A> 是一个很好的交换有关 Beehive 信息的地方。<BR><BR>
<LI>有关 Beehive 的更多信息，请访问 BEA Systems 公司的 <A href="http://dev2dev.bea.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Dev2Dev</A>。<BR><BR>
<LI>获取更多有关 <A href="http://www.eclipse.org/pollinate/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Eclipse Pollinate 项目</A> 的信息。<BR><BR>
<LI>参阅我即将出版的图书：<A href="http://www.amazon.com/exec/obidos/ASIN/1590595157/variouofmysit-20/103-9782801-7999867" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Pro Apache Beehive: With Eclipse Pollinate and SOA</A>。<BR><BR>
<LI><A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=1930110960" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Eclipse In Action: A Guide for Java Developers</A>（Independent Publishers Group，2003 年）是使用 Eclipse 的 Java 开发者的一本必读物。<BR><BR>
<LI>获取最新版本的 <A href="http://www.netbeans.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">NetBeans</A>，这是用来开发 Java 程序的一个开源的 IDE。<BR><BR>
<LI>在 <A href="http://www-128.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks Java 技术专区</A> 中寻找更多为 Java 开发者准备的资源。<BR><BR>
<LI>获取试用版的 DB2®、Lotus®、Rational、Tivoli® 和 WebSphere 产品，并在 IBM 中间件上开始构建和部署应用程序。选择免费的 Linux 或 Windows 版本的 <A href="http://www.ibm.com/developerworks/offers/sek/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Software Evaluation Kit (SEK)</A>。<BR><BR>
<LI>通过参与 <A href="http://www.ibm.com/developerworks/blogs/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks blogs</A> 加入 developerWorks 社区。<BR></LI></UL>
<P></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR><IMG height=80 alt="Kunal Mittal" src="http://www.ibm.com/developerworks/i/p-kmittal.jpg" width=64 align=left xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Kunal Mittal 是一位咨询顾问，他的特长是 Java、J2EE 和 Web 服务技术。他与别人合著了多本有关这些主题的书籍。他目前正在开发 Sony Pictures Entertainment 的门户项目，同时还在撰写一本有关 Apache Beehive 的书籍，该书将于 2005 年秋季出版。有关他的更多信息，请访问他的 <A href="http://www.soaconsultant.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Web 站点</A>。</TD></TR></TBODY></TABLE><BR clear=all><IMG height=10 alt="" src="http://www.ibm.com/i/c.gif" width=100 border=0><BR><img src ="http://www.blogjava.net/kapok/aggbug/4977.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-20 10:38 <a href="http://www.blogjava.net/kapok/archive/2005/05/20/4977.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>FAQ: 頁面流與NetUI</title><link>http://www.blogjava.net/kapok/archive/2005/05/20/4976.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 20 May 2005 02:15:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/20/4976.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4976.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/20/4976.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4976.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4976.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html">http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html</A><A href="http://dev2dev.bea.com.tw/techdoc/04wp/04wp_03122002.htm"></A><BR><BR><SPAN style="FONT-FAMILY: 宋体">要进一步了解</SPAN><SPAN>WebLogic Workshop</SPAN><SPAN style="FONT-FAMILY: 宋体">方面的信息，请参照我们</SPAN><SPAN><A href="http://dev2dev.bea.com/products/wlworkshop81/articles/workshopfaqs.jsp">WebLogic Workshop FAQ</A></SPAN><SPAN style="FONT-FAMILY: 宋体">。</SPAN>
<P style="pc5: "><SPAN>1．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#1"><SPAN style="FONT-FAMILY: 宋体">在页面流流视图中，为什么看不到由外部页面（即不属于当前页面流目录的页面）引发的动作？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>2．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#2"><SPAN style="FONT-FAMILY: 宋体">在</SPAN>WebLogic Express<SPAN style="FONT-FAMILY: 宋体">上，可以部署使用</SPAN>Java<SPAN style="FONT-FAMILY: 宋体">页面流（</SPAN>JPF<SPAN style="FONT-FAMILY: 宋体">）和</SPAN>NetUI<SPAN style="FONT-FAMILY: 宋体">标签库的</SPAN>web<SPAN style="FONT-FAMILY: 宋体">应用吗？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>3．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#3"><SPAN style="FONT-FAMILY: 宋体">页面流何时被销毁？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>4．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#4"><SPAN style="FONT-FAMILY: 宋体">如何访问被嵌套页面流的父（嵌套）页面流？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>5．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#5"><SPAN style="FONT-FAMILY: 宋体">一个</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">页面与</SPAN><SPAN style="FONT-FAMILY: 宋体">定义消息源的</SPAN>JPF<SPAN style="FONT-FAMILY: 宋体">文件不在同一个文件夹下，该</SPAN>JSP<SPAN style="FONT-FAMILY: 宋体">如何使用此</SPAN>JPF<SPAN style="FONT-FAMILY: 宋体">中</SPAN>&lt;netui:error&gt;<SPAN style="FONT-FAMILY: 宋体">元素中的消息源？</SPAN></A></P>
<P style="pc5: "><SPAN>6．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#6">Workshop 8.1<SPAN style="FONT-FAMILY: 宋体">支持</SPAN>Strut<SPAN style="FONT-FAMILY: 宋体">的</SPAN>DynaValidatorForm<SPAN style="FONT-FAMILY: 宋体">吗？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>7．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#7"><SPAN style="FONT-FAMILY: 宋体">如何为使用排序服务的网格设置初始排序？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>8．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#8"><SPAN style="FONT-FAMILY: 宋体">如何指定</SPAN>&lt;netui:imageButton&gt;<SPAN style="FONT-FAMILY: 宋体">元素中的“</SPAN>action<SPAN style="FONT-FAMILY: 宋体">”参数？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>9．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#9"><SPAN style="FONT-FAMILY: 宋体">在</SPAN>JSP<SPAN style="FONT-FAMILY: 宋体">中使用控件时，如何为其指定属性？</SPAN></A></SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>10．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#10"><SPAN style="FONT-FAMILY: 宋体">为什么存在允许我们使用</SPAN>pageContext.getAttribute<SPAN style="FONT-FAMILY: 宋体">和</SPAN>pageContext.setAttribute<SPAN style="FONT-FAMILY: 宋体">的</SPAN>&lt;netui:GetData&gt;<SPAN style="FONT-FAMILY: 宋体">标签，却不存在类似的</SPAN>&lt;netui:SetData&gt;<SPAN style="FONT-FAMILY: 宋体">标签？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>11．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#11"><SPAN style="FONT-FAMILY: 宋体">如何将动态数据传递给</SPAN>&lt;netui:methodParameter&gt;<SPAN style="FONT-FAMILY: 宋体">标签？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>12．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#12"><SPAN style="FONT-FAMILY: 宋体">如何使第三方或特定于应用的标签库出现在</SPAN>IDE <SPAN style="FONT-FAMILY: 宋体">选项板（</SPAN>Pallette<SPAN style="FONT-FAMILY: 宋体">）窗口中？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>13．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#13"><SPAN style="FONT-FAMILY: 宋体">当在</SPAN>netui-data:repeater<SPAN style="FONT-FAMILY: 宋体">中嵌套</SPAN>netui-data:repeater<SPAN style="FONT-FAMILY: 宋体">时，如何能够访问最内部</SPAN>repeater<SPAN style="FONT-FAMILY: 宋体">中的外部项的属性？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>14．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#14"><SPAN style="FONT-FAMILY: 宋体">有没有办法将</SPAN>NetUI<SPAN style="FONT-FAMILY: 宋体">标签中的</SPAN>XScript<SPAN style="FONT-FAMILY: 宋体">表达式绑定到</SPAN>jsp<SPAN style="FONT-FAMILY: 宋体">页面中的局部变量？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>15．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#15">JSLT<SPAN style="FONT-FAMILY: 宋体">与</SPAN>XScript<SPAN style="FONT-FAMILY: 宋体">之间有什么关系？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>16．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#16">NetUI<SPAN style="FONT-FAMILY: 宋体">的</SPAN>pageContext<SPAN style="FONT-FAMILY: 宋体">数据绑定作用域与</SPAN>javax.servlet.http.PageContext<SPAN style="FONT-FAMILY: 宋体">属性映射之间有什么关系？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>17．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#17"><SPAN style="FONT-FAMILY: 宋体">如何对</SPAN>NetUI<SPAN style="FONT-FAMILY: 宋体">标签库中的各种标签加以分类，以使其更好理解？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>18．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#18"><SPAN style="FONT-FAMILY: 宋体">页面流支持</SPAN>Struts Tiles<SPAN style="FONT-FAMILY: 宋体">吗？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>19．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#19"><SPAN style="FONT-FAMILY: 宋体">在页面流数据绑定作用域内，我能够使用的各种可用属性有哪些？</SPAN></A></SPAN></P>
<P style="pc5: "><SPAN>20．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/200312115.html#20"><SPAN style="FONT-FAMILY: 宋体">如何从当前页面流的</SPAN>JSP<SPAN style="FONT-FAMILY: 宋体">中调用不同页面流中的动作？</SPAN></A><BR><A name=1></A></SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">问：在页面流流视图（</SPAN><SPAN>PageFlow View</SPAN><SPAN style="FONT-FAMILY: 宋体">）中，为什么看不到由外部页面（即不属于当前页面流目录的页面）引发的动作？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：为了使页面流流视图（</SPAN><SPAN>PageFlow View</SPAN><SPAN style="FONT-FAMILY: 宋体">）看起来更加一目了然，</SPAN><SPAN>Workshop</SPAN><SPAN style="FONT-FAMILY: 宋体">在流视图中显示了对外部页面（不属于当前页面流目录的页面）的引用，但是没有显示任何由这些外部页面引发的动作。要查看由外部页面引发的动作，您需要在与相应页面所在的目录中打开页面流的流视图。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=2></A>问：在</SPAN><SPAN>WebLogic Express</SPAN><SPAN style="FONT-FAMILY: 宋体">上，可以部署使用</SPAN><SPAN>Java</SPAN><SPAN style="FONT-FAMILY: 宋体">页面流（</SPAN><SPAN>JPF</SPAN><SPAN style="FONT-FAMILY: 宋体">）和</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签库的</SPAN><SPAN>web</SPAN><SPAN style="FONT-FAMILY: 宋体">应用吗？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：答案是肯定的，使用</SPAN><SPAN>JPF</SPAN><SPAN style="FONT-FAMILY: 宋体">和</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签库的</SPAN><SPAN>web</SPAN><SPAN style="FONT-FAMILY: 宋体">应用可以被用在</SPAN><SPAN>WebLogic Express</SPAN><SPAN style="FONT-FAMILY: 宋体">（</SPAN><SPAN>WLX</SPAN><SPAN style="FONT-FAMILY: 宋体">）上。然而，您不能在</SPAN><SPAN>WLX</SPAN><SPAN style="FONT-FAMILY: 宋体">上使用控件，因为它们使用了</SPAN><SPAN>EJB</SPAN><SPAN style="FONT-FAMILY: 宋体">和</SPAN><SPAN>/</SPAN><SPAN style="FONT-FAMILY: 宋体">或</SPAN><SPAN>JMS</SPAN><SPAN style="FONT-FAMILY: 宋体">。在</SPAN><SPAN>Service Pack 2</SPAN><SPAN style="FONT-FAMILY: 宋体">上，页面流和</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签库将能够以一种独立于平台的方式为</SPAN><SPAN>servlet</SPAN><SPAN style="FONT-FAMILY: 宋体">容器（而不仅仅是</SPAN><SPAN>WebLogic</SPAN><SPAN style="FONT-FAMILY: 宋体">服务器）上的运行时部署所用。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=3></A>问：页面流何时被销毁？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：页面流被销毁的时候是：</SPAN></P>
<P style="pc5: "><SPAN>a.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">流移动到不同的、不可嵌套的页面流时</SPAN></P>
<P style="pc5: "><SPAN>b.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">被嵌套的页面流返回到嵌套它的页面流时</SPAN></P>
<P style="pc5: "><SPAN>c.<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">当会话超时并被销毁时。当前</SPAN><SPAN>Http</SPAN><SPAN style="FONT-FAMILY: 宋体">会话的生存期是页面流能够具有的最长生存期。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">注意，这里有一点很重要，即任何被嵌套的页面流都隐藏在嵌套页面流的“栈”中，并且对于当前被嵌套的页面流来说，无需返回到嵌套它的页面流，即可转到外部页面流（从而销毁了嵌套页面流的整个栈）。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=4></A>问：如何访问被嵌套页面流的父（嵌套）页面流？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：您可以使用下列方法从被嵌套的页面流中访问嵌套页面流。</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">PageFlowController parentPageFlow = PageFlowUtils.getNestingPageFlow(getRequest());</SPAN></P></TD></TR></TBODY></TABLE>
<P><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">如果函数</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">"PageFlowUtils.getNestingPageFlow( getRequest() )"</SPAN></P></TD></TR></TBODY></TABLE>
<P><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">返回</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial">null</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">，这就意味着被嵌套的页面流是被直接调用的，而不是通过父页面流调用的。</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=5></A>问：</SPAN><SPAN style="FONT-FAMILY: 宋体">一个</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">页面与定义消息源的</SPAN><SPAN>JPF</SPAN><SPAN style="FONT-FAMILY: 宋体">文件不在同一个文件夹下，该</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">如何使用此</SPAN><SPAN>JPF</SPAN><SPAN style="FONT-FAMILY: 宋体">中</SPAN><SPAN>&lt;netui:error&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">元素中的消息源？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：如果您正在</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">中引用的消息源被定义为“</SPAN><SPAN>test</SPAN><SPAN style="FONT-FAMILY: 宋体">”文件夹（位于</SPAN><SPAN>web</SPAN><SPAN style="FONT-FAMILY: 宋体">项目的根目录下）中页面流的默认消息源，那么通过在“</SPAN><SPAN>bundle</SPAN><SPAN style="FONT-FAMILY: 宋体">”名称之前添加其后带有文件夹名称的“</SPAN><SPAN>org.apache.struts.action.MESSAGE</SPAN><SPAN style="FONT-FAMILY: 宋体">”前缀，您可以像下面这样使用</SPAN><SPAN>&lt;netui:error&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">元素：</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:error bundle="org.apache.struts.action.MESSAGE/test" </SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">value="myerror"&gt;&lt;/netui:error&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="LINE-HEIGHT: 16.8pt; TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">或者</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:errors bundle="org.apache.struts.action.MESSAGE/test" &lt;/netui:errors&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">如果您已经在页面流中使用“</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial">err</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">”键定义了消息源，如下所示：</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;</SPAN>* @jpf:message-resources key="err" resources="validation.test.Errors"</P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">您需要将</SPAN><SPAN style="FONT-FAMILY: 宋体">“</SPAN><SPAN>org.apache.struts.action.MESSAGE</SPAN><SPAN style="FONT-FAMILY: 宋体">”字符串替换为键值，如下所示：</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:error bundle="err/test" value="myerror"&gt;&lt;/netui:error&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><A name=6></A><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">问：</SPAN><SPAN>Workshop 8.1</SPAN><SPAN style="FONT-FAMILY: 宋体">支持</SPAN><SPAN>Struts</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>DynaValidatorForm</SPAN><SPAN style="FONT-FAMILY: 宋体">吗？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：</SPAN><SPAN>Workshop </SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>8.1</SPAN><SPAN style="FONT-FAMILY: 宋体">版本不支持</SPAN><SPAN>Struts</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>DynaValidatorForm</SPAN><SPAN style="FONT-FAMILY: 宋体">。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=7></A>问：如何为使用排序服务的网格设置初始排序？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：在版本</SPAN><SPAN>8.1</SPAN><SPAN style="FONT-FAMILY: 宋体">中，</SPAN><SPAN>SortFilterService</SPAN><SPAN style="FONT-FAMILY: 宋体">不支持设置过滤器的初始排序。您始终可以在</SPAN><SPAN>JPF</SPAN><SPAN style="FONT-FAMILY: 宋体">中手动创建一个排序，或者在</SPAN><SPAN>RowSet</SPAN><SPAN style="FONT-FAMILY: 宋体">控件上的</SPAN><SPAN>SQL</SPAN><SPAN style="FONT-FAMILY: 宋体">中使用静态的</SPAN><SPAN>WHERE</SPAN><SPAN style="FONT-FAMILY: 宋体">或</SPAN><SPAN>ORDER BY</SPAN><SPAN style="FONT-FAMILY: 宋体">子句。关于这些方法惟一的限制是，当为页面添加书签时，排序与过滤器不会反映在所使用的</SPAN><SPAN>URL</SPAN><SPAN style="FONT-FAMILY: 宋体">中。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=8></A>问：如何指定</SPAN><SPAN>&lt;netui:imageButton&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">元素中的“</SPAN><SPAN>action</SPAN><SPAN style="FONT-FAMILY: 宋体">”参数？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：</SPAN><SPAN>&lt;netui:imageButton&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">元素不支持“</SPAN><SPAN>action</SPAN><SPAN style="FONT-FAMILY: 宋体">”参数。它总是前移到与表单相关的默认动作。您可以使用</SPAN><SPAN>&lt;netui:imageAnchor&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">元素作为代替，如下所示：</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:imageAnchor border="0" action="foo" src="/foo/bar.gif" width="68" height="24"/&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><A name=9></A><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">问：在</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">中使用控件时，如何为其指定属性？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：不幸的是，当前版本不支持这种行为。但存在其他两种选择：</SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>1．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">执行页面流中控件的方法，并将结果存放于</SPAN><SPAN>request</SPAN><SPAN style="FONT-FAMILY: 宋体">及动作表单中传递给相应的页面。</SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>2．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">使用</SPAN><SPAN>callControl</SPAN><SPAN style="FONT-FAMILY: 宋体">标签的控制属性中的表达式，直接绑定到一个控件，该控件在页面流中被公开为如下形式：</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui-data:callControl controlId="{pageFlow.myDatabaseControl}" ... /&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">接下来，您可以使用页面流中控件实例上的注解。</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体"><A name=10></A>问：</SPAN><SPAN style="FONT-FAMILY: 宋体">为什么存在允许我们使用</SPAN><SPAN>pageContext.getAttribute</SPAN><SPAN style="FONT-FAMILY: 宋体">和</SPAN><SPAN>pageContext.setAttribute</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>&lt;netui:GetData&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">标签，却不存在类似的</SPAN><SPAN>&lt;netui:SetData&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">标签？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：</SPAN><SPAN>getData</SPAN><SPAN style="FONT-FAMILY: 宋体">标签的作用是，允许</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">作者针对由于数据绑定而产生的对象书写</SPAN><SPAN>scriptlet</SPAN><SPAN style="FONT-FAMILY: 宋体">。我们不提供</SPAN><SPAN>setData</SPAN><SPAN style="FONT-FAMILY: 宋体">标签，因为我们要使程序设计模型和工具继续支持它，使其专注于在页面流中添加业务</SPAN><SPAN>/</SPAN><SPAN style="FONT-FAMILY: 宋体">状态维护逻辑。任何对</SPAN><SPAN>(session|request).setAttribute</SPAN><SPAN style="FONT-FAMILY: 宋体">的调用多半应该在一个动作</SPAN><SPAN>(action)</SPAN><SPAN style="FONT-FAMILY: 宋体">内完成。这可以避免页面出现副作用，并使它们更加容易维护。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=11></A>问：如何将动态数据传递给</SPAN><SPAN>&lt;netui:methodParameter&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">标签？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：我们可以使动态数据可以为</SPAN><SPAN style="COLOR: #333333">&lt;netui:methodParameter&gt;</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">标签所用，具体方法是从</SPAN><SPAN style="COLOR: #333333">pageContext</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">变量来设置并访问它，如下所示：</SPAN><SPAN style="COLOR: #333333"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;%</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">String s = "name";</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">pageContext.setAttribute("theName", s);</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">%&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui-data:callControl controlId="productsDBControl"</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">method="insertProduct"&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;</SPAN>&lt;netui-data:methodParameter value="{pageContext.theName}"/&gt;&gt;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;/netui-data:callControl&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><A name=12></A><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">问：如何使第三方或特定于应用的标签库出现在</SPAN><SPAN>IDE </SPAN><SPAN style="FONT-FAMILY: 宋体">选项板（</SPAN><SPAN>Pallette</SPAN><SPAN style="FONT-FAMILY: 宋体">）窗口中？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：要在</SPAN><SPAN>IDE</SPAN><SPAN style="FONT-FAMILY: 宋体">中为</SPAN><SPAN>Pallette</SPAN><SPAN style="FONT-FAMILY: 宋体">窗口添加一个标签库，您需要：</SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>1．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">将标签库</SPAN><SPAN>JAR</SPAN><SPAN style="FONT-FAMILY: 宋体">添加到相关项目的</SPAN><SPAN>WEB-INF/lib</SPAN><SPAN style="FONT-FAMILY: 宋体">目录中。</SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>2．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">在</SPAN><SPAN>web.xml</SPAN><SPAN style="FONT-FAMILY: 宋体">中添加一项使其指向标签库的</SPAN><SPAN>TLD</SPAN><SPAN style="FONT-FAMILY: 宋体">文件，并赋予相应的</SPAN><SPAN>URI</SPAN><SPAN style="FONT-FAMILY: 宋体">，这使得</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">文件通过该</SPAN><SPAN>URI</SPAN><SPAN style="FONT-FAMILY: 宋体">引用</SPAN><SPAN>TLD</SPAN><SPAN style="FONT-FAMILY: 宋体">。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">接着，您可以打开一个</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">，查看</SPAN><SPAN>Insert</SPAN><SPAN style="FONT-FAMILY: 宋体">菜单或标签选项板，以确保标签库中的标签能够显示出来。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=13></A>问：当在</SPAN><SPAN>netui-data:repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">中嵌套</SPAN><SPAN>netui-data:repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">时，如何能够访问最内部</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">中的外部项的属性？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：您可以访问最内部</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">中外部</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">的属性，具体方法是使用一个形如“</SPAN><SPAN>{container.container.*}</SPAN><SPAN style="FONT-FAMILY: 宋体">”的表达式，其中第一个容器引用当前的</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">，而第二个容器则引用其父</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">，等等。还有另外一种办法，通过使用</SPAN><SPAN>netui-data:getData</SPAN><SPAN style="FONT-FAMILY: 宋体">标签，您可以在页面上下文中的外部</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">中设置一个变量，并在形如</SPAN><SPAN>{pageContext.*}</SPAN><SPAN style="FONT-FAMILY: 宋体">的被嵌套的</SPAN><SPAN>repeater</SPAN><SPAN style="FONT-FAMILY: 宋体">中访问该变量。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=14></A>问：有没有办法将</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签中的</SPAN><SPAN>XScript</SPAN><SPAN style="FONT-FAMILY: 宋体">表达式绑定到</SPAN><SPAN>jsp</SPAN><SPAN style="FONT-FAMILY: 宋体">页面中的局部变量？</SPAN></P>
<P style="LINE-HEIGHT: 16.8pt; TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：</SPAN><SPAN>&lt;netui-data:getData&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">标签可以用于计算</SPAN><SPAN>XScript</SPAN><SPAN style="FONT-FAMILY: 宋体">表达式的值，并将其放置到</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>pageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">中。接下来就可以在</SPAN><SPAN>scriptlet</SPAN><SPAN style="FONT-FAMILY: 宋体">中访问</SPAN><SPAN>pageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">中的变量。例如，</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui-data:getData resultId="first" value="{actionForm.firstname}"/&gt;</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>&lt;% String firstName = (String) pageContext.getAttribute("first"); %&gt;</P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><A name=15></A><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">问：</SPAN><SPAN>JSLT</SPAN><SPAN style="FONT-FAMILY: 宋体">与</SPAN><SPAN>XScript</SPAN><SPAN style="FONT-FAMILY: 宋体">之间有什么关系？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：在当前的版本中，</SPAN><SPAN>JSTL</SPAN><SPAN style="FONT-FAMILY: 宋体">的表达式语言（</SPAN><SPAN>EL</SPAN><SPAN style="FONT-FAMILY: 宋体">）与</SPAN><SPAN>XScript</SPAN><SPAN style="FONT-FAMILY: 宋体">之间没有任何关系。您可以在需要的时候使用</SPAN><SPAN>JSTL</SPAN><SPAN style="FONT-FAMILY: 宋体">标签，而脚本语言不会彼此干扰。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=16></A>问：</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>pageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">数据绑定作用域与</SPAN><SPAN>javax.servlet.http.PageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">属性映射之间有什么关系？</SPAN></P>
<P style="LINE-HEIGHT: 16.8pt; TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">的</SPAN><SPAN>pageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">数据绑定作用域只不过是</SPAN><SPAN>javax.servlet.http.PageContext</SPAN><SPAN style="FONT-FAMILY: 宋体">属性映射顶部的一层而已。这个上下文的定义是，它访问页面作用域内的名称</SPAN><SPAN>/</SPAN><SPAN style="FONT-FAMILY: 宋体">值对的映射。例如，下面这种形式是合法的：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;%</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>pageContext.setAttribute("myPageContextVar", "A Foo String");</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">%&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">接下来的这两种形式是完全等同的：</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:content value="{pageContext.myPageContextVar}"/&gt;</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">和</SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 5.5pt; COLOR: #333333; FONT-FAMILY: Verdana"><BR></SPAN><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;% out.write(pageContext.getAttribute("myPageContextVar"); %&gt;</SPAN><SPAN style="FONT-SIZE: 5.5pt; COLOR: #333333; FONT-FAMILY: Verdana"></SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><A name=17></A><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">问：</SPAN><SPAN style="FONT-FAMILY: 宋体">如何对</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签库中的各种标签加以分类，以使其更好理解？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：对</SPAN><SPAN>NetUI</SPAN><SPAN style="FONT-FAMILY: 宋体">标签库值的标签进行分类的基本方法是，这些标签是只读的，还是可读写的（即是否与服务器进行数据的交互）。</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">基于这标准，标签可以被划分为三类：</SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>a．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">只读</SPAN> <SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial">Read-Only</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">只读标签有</SPAN><SPAN>&lt;netui:content&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">和</SPAN><SPAN>&lt;netui:label&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">等等。它们从标签中的“</SPAN><SPAN>value</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性中读取数据，但不会将数据发回给服务器。</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left; pc5: " align=left><SPAN>b．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">读写单值</SPAN> <SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial">Read-Write Single Value</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">这一类的标签有</SPAN><SPAN>&lt;netui:textbox&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">、</SPAN><SPAN>&lt;netui:textarea&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">、</SPAN><SPAN>&lt;netui:checkbox&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">等等。这些标签需要了解两件事情：</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">在</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">呈现时，在何处获取数据片断。</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: Wingdings">l<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">在请求提交时，将数据片断放置到何处。</SPAN><SPAN></SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">“数据片断”即为“</SPAN><SPAN>dataSource</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性的值。请注意，这与</SPAN><SPAN>javax.sql.Datasource</SPAN><SPAN style="FONT-FAMILY: 宋体">对象没有任何关系。</SPAN><SPAN></SPAN></P>
<P style="pc5: "><SPAN>c．<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体">读写多值</SPAN> <SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial">Read-Write multiple value</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">这一类标签包括</SPAN><SPAN>&lt;netui:checkBoxGroup&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">、</SPAN><SPAN>&lt;netui:select&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">等等，它们不过是上面一类标签的多值版本罢了。它们同样需要了解从何处获取各种选项，以便从服务器读取并显示给用户。它们可以从“</SPAN><SPAN>optionsDataSource</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性中获得。“</SPAN><SPAN>dataSource</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性被用于引用将被写回到服务器上的数据。</SPAN><SPAN> </SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=18></A>问：页面流支持</SPAN><SPAN>Struts Tiles</SPAN><SPAN style="FONT-FAMILY: 宋体">吗？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：在当前的版本中，页面流不支持下面这种做法，即通过在页面流中使用</SPAN><SPAN>Struts-merge</SPAN><SPAN style="FONT-FAMILY: 宋体">功能以求在页面流中使用</SPAN><SPAN>Struts Tiles</SPAN><SPAN style="FONT-FAMILY: 宋体">。这是因为</SPAN><SPAN>Tiles</SPAN><SPAN style="FONT-FAMILY: 宋体">要求使用它自己的</SPAN><SPAN>RequestProcessor</SPAN><SPAN style="FONT-FAMILY: 宋体">，而这会干扰页面流</SPAN><SPAN>RequestProcessor</SPAN><SPAN style="FONT-FAMILY: 宋体">。然而，我们的确支持</SPAN><SPAN>Tiles</SPAN><SPAN style="FONT-FAMILY: 宋体">模块与页面流在同一个</SPAN><SPAN>Web</SPAN><SPAN style="FONT-FAMILY: 宋体">项目中共存与交互。</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体"><A name=19></A>问：在页面流数据绑定作用域内，我能够使用的各种可用属性有哪些？</SPAN></P>
<P><SPAN style="FONT-FAMILY: 宋体">答：在页面流数据绑定作用域内，可用的属性包括任何公共字段，以及任何开发人员在特定页面流中定义的公共</SPAN><SPAN>JavaBean</SPAN><SPAN style="FONT-FAMILY: 宋体">属性。例如，下面的页面流定义了可以与标签属性中的“</SPAN><SPAN>{pageFlow.name}</SPAN><SPAN style="FONT-FAMILY: 宋体">”绑定的“</SPAN><SPAN>name</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性。</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">public class Controller extends PageFlowController</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">{</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>public String name;</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>/**</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @jpf:action</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>* @jpf:forward name="index" path="index.jsp"</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>*/</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>protected Forward begin()</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>{</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>return new Forward("index");</P>
<P style="TEXT-ALIGN: left" align=left><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>}</P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">}</SPAN></P></TD></TR></TBODY></TABLE>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体"><A name=20></A>问：如何从当前页面流的</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">中调用不同页面流中的动作？</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-FAMILY: 宋体">答：要从当前页面流的</SPAN><SPAN>JSP</SPAN><SPAN style="FONT-FAMILY: 宋体">中调用不同页面流中的动作，您可以使用</SPAN><SPAN>&lt;netui:anchor&gt;</SPAN><SPAN style="FONT-FAMILY: 宋体">标签中的“</SPAN><SPAN>href</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性。（注意，您不能使用“</SPAN><SPAN>action</SPAN><SPAN style="FONT-FAMILY: 宋体">”属性，因为它只能用于调用当前页面流中的动作）。您可以构造遵循模式的</SPAN><SPAN>URI</SPAN><SPAN style="FONT-FAMILY: 宋体">。</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">"&lt;directory&gt;/&lt;action&gt;.do", </SPAN></P></TD></TR></TBODY></TABLE>
<P style="LINE-HEIGHT: 16.8pt; TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial"><BR></SPAN><SPAN style="COLOR: #333333; FONT-FAMILY: 宋体">例如：</SPAN><SPAN style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: Arial">.,</SPAN></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=Normal bgColor=#cccccc>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">&lt;netui:anchor href="/otherPageFlow/myMethodOne.do"&gt;Do Method</SPAN></P>
<P style="TEXT-ALIGN: left" align=left><SPAN style="FONT-SIZE: 12pt; COLOR: #333333; FONT-FAMILY: 宋体">One&lt;/netui:anchor&gt;</SPAN></P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/4976.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-20 10:15 <a href="http://www.blogjava.net/kapok/archive/2005/05/20/4976.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据绑定101 </title><link>http://www.blogjava.net/kapok/archive/2005/05/20/4975.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 20 May 2005 02:13:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/20/4975.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4975.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/20/4975.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4975.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4975.html</trackback:ping><description><![CDATA[<STRONG><A href="http://dev2dev.bea.com.cn/techdoc/wlportal/20031027.html">http://dev2dev.bea.com.cn/techdoc/wlportal/20031027.html</A><BR><BR>简介<BR></STRONG>页面流提供了一种方便构建Web应用业务和导航逻辑的编程模型。为了显示和修改页面流动作所收集的数据，WebLogic Workshop 8.1 数据绑定框架将用户界面中的NetUI JSP标签绑定到Web应用数据上。两者相互结合为构建基于Web的用户界面提供了一种令人瞩目的开发模型。<BR>数据绑定是一个重要主题；这里我们将介绍用于绑定、显示、更新数据的表达式语言和JSP标签；并将演示如何创建一个简单的表单。本文假定读者对基本页面流和JSP程序设计较为熟悉。文中用到的所有代码都可以从附带的下载文件<A href="http://ftpna2.bea.com/pub/downloads/dev2devDatabindingBasics.zip">Web application</A> (1.4 MB)中获得。<BR><BR><B>参与者 </B><BR>绑定UI到数据的过程需要三个参与者--业务对象/数据、NetUI JSP标签库、表达式语言。业务对象中含有需要绑定到JSP页面的数据。JSP标签以特定视图（只读或可更新）的方式在Web浏览器中绘制数据，而表达式语言则将两者粘合在一起，从而可以通过JSP标签引用业务对象的属性。借助三个参与者的实例，Workshop开发人员可以用灵活且优雅的方式创建用于显示、更新、及创建数据的用户界面。<BR>整篇文章我们都会用到一个定义了字段和属性的JavaBean，属性更适合说明数据是如何绑定到不同类型之上的。该表单的一部分如下；其中定义了一个公共属性name 和一个公共字段eMail。可以把该类看作一个针对简单Web页面的业务对象，该Web页面演示了如何绑定到表单的属性。<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>public class Customer<BR>{<BR>　public String eMail = "";<BR>　private String name;<BR><BR>　public String getName()<BR>　{<BR>　return this.name;<BR>　}<BR>　. . .<BR>　}<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/Controller.jpf] </TD></TR></TBODY></TABLE><BR>URL 也是该bean的一个属性。除此之外，同一页面流中还定义了一个LocationInfo 类，它暴露了可以进行数据绑定的复杂类型。<BR>为了更快将读者引进门，我们假定代表用户"Dave Smith"的Customer对象已经存在，并且可以通过String类型的关键字"customer"在request的属性映射中进行访问。这个Customer的姓名可以借助JSP的NetUI标签显示在Web页中。<BR>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>Customer Name: &lt;netui:label value="{request.customer.name}"/&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/simple.jsp] </TD></TR></TBODY></TABLE><BR>它在Workshop JSP 设计器中显示如下：<BR>
<TABLE cellSpacing=0 cellPadding=2 width=250 align=center border=1>
<TBODY>
<TR>
<TD>Customer Name: {request.customer.name}</TD></TR></TBODY></TABLE><BR>并生成如下的HTML输出： <BR>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>Customer Name: &lt;span&gt;John Smith&lt;/span&gt;</TD></TR></TBODY></TABLE><BR>下面我们将从整体上更进一步分析这个例子和表达式。 
<P><B>表达式语言</B><BR>表达式语言使得Workshop开发人员能够使用一种简单的语法在JSP页面中引用业务对象的数据。通常，一个表达式引用业务对象上的一个或一系列属性，从而可以唯一标识JSP标签所绑定的数据。在以上例子中，表达式{request.customer.name}引用Customer JavaBean中的name 属性，保存在request中的该JavaBean被称作"customer"。<BR>在JSP标签属性中，表达式分别以'{' 和'}'作为开始和结束标记。标签探测一个属性是否是表达式、或者其中是否包含表达式，从而对属性进行求值。<BR>一个表达式含有两个独立的部分--数据绑定上下文：在其中进行表达式求值；属性：它是表达式求值返回的结果。在表达式{request.customer.name}中， request是绑定上下文，它引用JSP页面request的属性映射。表达式剩下的部分用来引用唯一的对象属性。"customer"标识符用来在request的属性映射中查找相关对象，name标识符用来获取Customer 对象上的JavaBean 属性name。从功能上讲，该表达式等同于以下语句： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>String name = ((Customer)request.getAttribute("customer")).getName();</TD></TR></TBODY></TABLE>
<P>同样是引用相同的数据，表达式语法更为简单。<BR>除了request 上下文，还可以在其它数据绑定上下文中求值表达式，这些上下文用来访问Web应用和页面流环境的不同部分。绑定上下文还可以引用其它资源，比如通过url 或 bundle 上下文，它可以分别引用URL参数或资源包中的国际化字符串。完整的上下文及其引用对象/资源的列表如下：<BR></P>
<TABLE cellSpacing=1 cellPadding=2 width="80%" border=1>
<TBODY>
<TR>
<TD>上下文名称</TD>
<TD>上下文引用的对象</TD></TR>
<TR>
<TD>actionForm</TD>
<TD>和当前NetUI 表单标签相关的动作表单</TD></TR>
<TR>
<TD>pageFlow</TD>
<TD>某个用户的当前页面流</TD></TR>
<TR>
<TD>globalApp</TD>
<TD>webapp中某个用户的Global.app</TD></TR>
<TR>
<TD>bundle</TD>
<TD>用于引用声明或隐含的资源包属性</TD></TR>
<TR>
<TD>pageContext</TD>
<TD>JSP 页面的PageContext属性映射</TD></TR>
<TR>
<TD>request</TD>
<TD>request的属性映射</TD></TR>
<TR>
<TD>session</TD>
<TD>session的属性映射</TD></TR>
<TR>
<TD>application</TD>
<TD>servlet上下文的属性映射</TD></TR>
<TR>
<TD>url</TD>
<TD>用于当前请求的URL 查询参数</TD></TR>
<TR>
<TD>container</TD>
<TD>当使用重复标签绘制某个数据集时，复杂数据绑定标签用它来访问当前条目</TD></TR></TBODY></TABLE>
<P>现在进一步讨论上表中提到的一些上下文。<BR>同样是使用表达式，仍然存在多种不同方法来引用同一对象的同一属性。例如，在我们的例子中，以下几个表达式都是等价的：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>- request["customer"]["name"] <BR>- request.customer.name<BR>- request["customer"].name<BR>- request.customer["name"]<BR>　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp] </TD></TR></TBODY></TABLE>
<P>而且，表达式既可以访问字段，也可以访问属性，这些属性返回任意基本类型或Java Object类型，也可以返回它们的数组、列表和映射类型。一般说来，属性命名遵循JavaBean属性的setter和getter方法命名规范。关于暴露、命名、引用数据绑定对象属性的特殊规则将在下面进行讨论。<BR>注意：对象的值既可以是只读的也可以是可读/写的；对象对应的值的暴露方式决定了它对于表达式仅是只读的，还是当Web浏览器向服务器提交数据时该值是可更新的。</P>
<P><B>访问属性</B><BR>为了识别类暴露的可绑定数据属性，表达式语言采用了JavaBean命名规范。符合以下条件的方法可以暴露一个只读属性： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>- public 访问权限 <BR>- 返回非void类型<BR>- 无参数<BR>- 以"get"作为名称的开头 </TD></TR></TBODY></TABLE>
<P>可以使用表达式来引用数据绑定上下文中符合以上条件的任意方法。JavaBean属性的名称被包含在表达式中。例如，类中定义了一个属性： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>public String getName();<BR>　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf] </TD></TR></TBODY></TABLE>
<P>在表达式中它被引用为： </P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>{request.customer.name}<BR>　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp] </TD></TR></TBODY></TABLE>
<P>前提是一个Customer 对象被指定为“customer”并可以从request中得到。</P>
<P>在本例中，方法 getName 对应名为name的JavaBean属性；这是通过去掉“get”并将第一个字符变成小写之后得到的。一个例外是如果“get”之后的头两个字符都是大写的，JavaBean属性的名称将在表达式中保持不变。例如，属性：</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>public String getURL();<BR>　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf] </TD></TR></TBODY></TABLE>
<P>在表达式中被引用为： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>{request.customer.URL}<BR>　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp] </TD></TR></TBODY></TABLE>
<P>迄今为止，以上的name 和URL 属性都是只读的，因为我们没有为它们定义setter方法，该方法可以用来更新对象的属性值。一个JavaBean的setter方法需要符合以下条件： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>- 以"set"作为名称开头<BR>- 返回void类型<BR>- 只有一个参数，它的类型与对应getter方法的返回类型相同 </TD></TR></TBODY></TABLE>
<P>前面两个属性的JavaBean setter如下：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>public void setName(String name);<BR>public void setURL(String url);<BR>　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf]<BR></TD></TR></TBODY></TABLE><BR>如果对象中同时存在正确定义的getter 和setter，属性就是可读/写的。这种类型的属性十分重要，因为可以从Web页面更新它们的值。 
<P>访问字段 </P>
<P>可以通过表达式访问公共字段。上面Customer类中定义的“eMail”字段就是一个例子。公共字段总是可读/写的。Customer的email地址在Web页中可以显示为：</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>&lt;netui:label value="{request.customer.eMail}"/&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf]<BR></TD></TR></TBODY></TABLE><BR>访问数组或列表中的元素 
<P></P>
<P>使用常用的[index] 语法可以引用数组或列表中的条目。如果一个属性被暴露为：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>public String[] getZipArray();<BR>[binding/Controller.jpf]<BR>[binding/index.jsp]<BR></TD></TR></TBODY></TABLE>
<P>那么可以通过以下表达式访问数组的第四个条目： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>{request.locationInfo.zipArray[3]}<BR>[binding/index.jsp]<BR>[binding/index.jsp] </P></TD></TR></TBODY></TABLE>
<P>假定LocationInfo对象可以在request中通过关键字“locationInfo”进行访问，<BR></P>
<P>如果一个属性被暴露为：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>public List getZipList();<BR>　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf] </P></TD></TR></TBODY></TABLE>
<P>那么以下表达式将会访问列表中的第四个条目： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>{request.locationInfo.zipList[3]}<BR>[binding/index.jsp]<BR>[binding/index.jsp] </P></TD></TR></TBODY></TABLE>
<P>此类表达式求值还是略有不同。如果该表达式在一个长度仅为2的String[]上被求值，因为数组的长度小于表达式的引用长度，所以将会发生错误。如果属性是列表类型，表达式将被简单地求值为null。</P>
<P>使用NetUI Repeater标签集可以方便地绘制数组和列表。稍候一篇dev2dev文章将会涉及该标签集以及“container”绑定上下文。</P>
<P>访问映射中的元素 </P>
<P>表达式还可以采用与访问属性相同的语法来引用映射中特定键值对应的条目。例如，如果LocationInfo 类暴露了一个含有州名缩写和州名的Map，并将其定义为带有JavaBean getter方法的可绑定数据属性：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>public Map getAbbrevMap();<BR>　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/Controller.jpf] </P></TD></TR></TBODY></TABLE>
<P>那么，以下两个表达式都可以使用键值“CO”在映射中进行查找： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>{request.locationInfo.abbrevMap.CO}<BR>{request.locationInfo.abbrevMap["CO"]}<BR>　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp]<BR></P></TD></TR></TBODY></TABLE>
<P>访问基本类型 </P>
<P>采用以上任何机制暴露的数据类型可以是Java对象或基本类型。如果某个属性或字段的类型是基本类型，比如int 或 boolean ，表达式的求值结果将是基本类型的Java对象包装器。在本例中，将分别是Integer 和 Boolean。</P>
<P></P>
<P>表达式和NetUI JSP标签 </P>
<P>在NetUI JSP标签上使用表达式、属性可以引用业务对象属性，并将数据显示在Web页面上。同一属性的显示方式取决于标签的选择。比如，绑定到NetUI label的字符串属性是只读的，而绑定到NetUI form内NetUI text box的字符串属性却是可读/写的。</P>
<P>目前存在三个NetUI标签库——HTML、复杂数据绑定、模板，本文不涉及后面两个。HTML标签集尽可能遵循HTML4.01规范并将自己的标签和HTML中定义的元素相互结合。例如，textBox、 checkBox、 和radioButton 标签分别显示HTML输入标签类型“text”、 “checkbox”、 和“radio”。可以在Workshop标签面板中找到这些标签。</P>
<P></P>
<P><IMG height=388 src="http://dev2dev.bea.com.cn/images/article/portal031013/image002.jpg" width=284><BR><BR>也可以在Workshop 8.1的Insert菜单中找到这些标签。</P>
<P>NetUI HTML 标签进一步可以分为两类——只能显示数据的标签（或称为只读的）、能够在服务器和Web浏览器之间“往返”的标签（或称为可读/写的）。可以使用诸如netui:form 内的textBox这类标签在服务器和客户端之间传递数据。关于此类标签如何绑定数据，彼此之间也存在不同。</P>
<P>只读NetUI标签 </P>
<P>只读标签使用表达式绑定属性“value”来指定在页面中绘制的数据。此类标签只是简单地通过表达式从业务对象中读取数值并在Web页面上绘制，可能还会对输出结果进行格式处理。我们已经知道netui:label 标签可以在页面上绘制表达式的值，其实它也可以绘制普通文本： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>&lt;netui:label value="Blue"/&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp]<BR></P></TD></TR></TBODY></TABLE>
<P>因为label标签不是到服务器的可往返标签，所以它的value属性可以是文本和表达式的组合。例如，想要显示用反斜杠分开的客户姓名和年龄，可以这样使用label： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>&lt;netui:label value="{request.customer.age} / {request.customer.age}"/&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[customerEntry/display.jsp]<BR></P></TD></TR></TBODY></TABLE>
<P>每个表达式都将被求值，文本“/”将会混合到表达式结果中。</P>
<P><BR>可读/写NetUI标签</P>
<P>第二组标签使用“dataSource”属性，它们比较特殊因为被“dataSource”属性引用的数据可以在Web浏览器和服务器之间往返。尽管dataSource属性可以绑定表达式，但该属性的value必须是一个纯粹的表达式。与label标签的value属性不同，dataSource属性不能混合文本和表达式，因为dataSource属性的value被用在两个地方：</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>- 绘制JSP页面的时候通过表达式读取值<BR>- 当表单所在页面POST的时候，通过同一个表达式将值写回到业务对象中去。<BR></P></TD></TR></TBODY></TABLE>
<P>采取这种方式，诸如textBox这样的标签就可以在服务器和Web浏览器之间来回传输数据。<BR>如果在dataSource 属性中混合使用文本，属性就不是一个纯粹的表达式并将报错。因为dataSource 属性通常被用于同数据库交换数据，所以这里使用的表达式必须引用那些可以更新的数据绑定上下文，它们是actionForm、pageFlow、和 globalApp。 除此以外，所有其它绑定上下文在数据被POST到服务器时都是只读的。</P>
<P>一个简单例子<BR>让我们对前面的Customer例子稍作修改并利用NetUI编写一个“Hello World”，从而形成一个实用的例子。本例中含有一个页面流simpleForm 和一个JSP。JSP定义了一个绑定到页面流中某个action的NetUI表单。当表单从浏览器中POST的时候，动作表单将被创建，提交的数据将被添加到动作表单中。接着，输入页面被刷新，刚刚在只读label中显示的信息将会显示到可编辑的文本框中。这里所说的页面流是指本文示例应用中的simpleForm。</P>
<P>首先我们来看看动作表单，它们负责封装那些在页面中被显示并更新的数据。该类定义了通过NetUI HTML在JSP表单标签内可编辑的属性；它暴露了唯一一个属性——message。</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>public static class MessageForm extends FormData<BR>{<BR>　　private String message;<BR><BR>　　public void setMessage(String message)<BR>　　{<BR>　　　this.message = message;<BR>　　}<BR><BR>　　public String getMessage()<BR>　　{<BR>　　return this.message;<BR>　　}<BR>}<BR>　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[simpleForm/Controller.jpf]<BR></P></TD></TR></TBODY></TABLE>
<P>接着我们来看看JSP页面。这里的netui:form 标签用于引用在表单POST时将会执行的页面流动作。如果您对页面流不熟悉，请参阅参考材料部分的页面流参考手册。一般来说，从可读/写NetUI标签POST到服务器的更新必须在NetUI form 标签中进行。<BR><BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>&lt;netui:form action="echoMessage" focus=""&gt;<BR>　&lt;table&gt;<BR>　　&lt;tr&gt;<BR>　　　&lt;td&gt;Current message:&lt;/td&gt;<BR>　　　&lt;td&gt;&lt;netui:label value="{actionForm.message}"/&gt;&lt;/td&gt;<BR>　　&lt;/tr&gt;<BR>　&lt;tr&gt;&lt;td colspan="2"&gt; &lt;/td&gt;&lt;/tr&gt;<BR>　&lt;tr&gt;<BR>　　　&lt;td&gt;&lt;netui:label value="Message:"/&gt;&lt;/td&gt;<BR>　　　&lt;td&gt;&lt;netui:textBox dataSource="{actionForm.message}"/&gt;&lt;/td&gt;<BR>　&lt;/tr&gt;<BR>&lt;/table&gt;<BR>&lt;netui:button value="echoMessage"/&gt;<BR>&lt;/netui:form&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[simpleForm/index.jsp]<BR></P></TD></TR></TBODY></TABLE>
<P>JSP中有两个附加NetUI标签；其中netui:textBox 利用表达式{actionForm.message}在当前动作表单中显示message的值。最初该值以及textBox都是空的。还有一个button标签用来把表单提交到服务器。运行例子的时候，请在文本框中输入“Hello World”并按下“Submit”。<BR><BR>看！当表单提交到服务器时，页面流生命周期创建了接收POST数据（即本例中的message）的动作表单。因为textBox的dataSource 属性对应的表达式被映射为HTML输入标签——{actionForm.message}，所以message的值被路由到该属性上。</P>
<P>一旦表单填充完毕，postback 动作就会执行并在页面中重新绘制相同的表单。运行结果是：表达式{actionForm.message} 现在同时指向label和textBox 中显示的“Hello World”。在文本框中输入文本并重新提交表单可以修改该值。</P>
<P><B>技巧和经验</B></P>
<P></P>
<P>如何访问页面流的成员属性？ </P>
<P>页面流可以像其它类一样暴露JavaBean属性；利用表达式和pageFlow 绑定上下文可以绑定这些属性。例如，如果一个页面流用以下方法暴露属性productName：</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>public String getProductName()<BR>{<BR>return productName; <BR>}<BR>　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[howdoi/Controller.jpf] </P></TD></TR></TBODY></TABLE>
<P>就可以通过以下表达式在JSP中进行数据绑定： <BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>{pageFlow.productName}<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[howdoi/index.jsp] </P></TD></TR></TBODY></TABLE>
<P>你还可以绑定到页面流暴露的公共字段。</P>
<P></P>
<P>应该在页面流上暴露映射吗？<BR>在任何可以接受POST数据的数据绑定上下文中暴露Map类型都是危险的。问题在于映射可以变得很大，这就在服务上开了一个安全漏洞：因为网页可以直接POST数据到Map，这将会导致map无限变大、消耗掉可观的服务器资源。<BR>通过类似request这样的只读数据绑定上下文来暴露Map类型要安全很多。上面的例子中，LocationInfo对象通过abbrevMap 属性实现了这点。</P>
<P>如果在表单上暴露一个int 类型的属性，从页面POST出去的是什么类型？</P>
<P>当JSP页面POST数据到服务器时，所有的数据都以字符串形式到达。应用POST数据到页面流属性或动作表单的过程中，NetUI会设法把已经POST到底层属性或字段的字符串数据转换为原有的类型。<BR>例如，如果动作表单暴露一个类型为int、名称为 age 的属性，并且该字段可以从NetUI表单更新，类型转换工作即是把String 类型的“42”的转换成int 类型的42。如果转换失败，失败信息将会显示在运行WebLogic的命令提示窗口中。</P>
<P><BR>如何显示一个数据列表或数组？</P>
<P>利用NetUI复杂数据绑定标签可以绘制诸如列表、数组、映射、和行集这类的数据集。这些将在接下来的dev2dev文章中讨论。</P>
<P><BR>表达式求值失败会怎样？<BR>当JSP准备绘制时，如果一个表达式求值失败了，JSP页面上将会显示一个失败信息。假定有属性绑定的例子：</P>
<P></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>Customer Name: &lt;netui:label value="{request.customer.name}"/&gt;<BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp] </P></TD></TR></TBODY></TABLE>
<P>如果request的属性映射中不包含名为“customer”的对象，那么绑定将会失败，因为name属性无法访问null对象。请尝试将例子改为：<BR></P>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>Customer Name: <BR>　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　[binding/index.jsp]<BR>[binding/index.jsp] </P></TD></TR></TBODY></TABLE>
<P>这样就可以知道表达式求值是如何失败的了。</P>
<P></P>
<P>Workshop 8.1 IDE如何帮我进行数据绑定？</P>
<P>Workshop可以用多种方式帮助JSP作者进行数据绑定，其中最重要的一种是通过数据面板。使用数据面板可以在JSP页面中创建能够显示或更新数据的NetUI标签。</P>
<P>例如，在Workshop中打开JSP页面howdoi/index.jsp；在页面上，数据面板显示如下：</P>
<P></P><IMG height=225 src="http://dev2dev.bea.com.cn/images/article/portal031013/image004.jpg" width=260> <BR>您可以看见productName属性在面板中是可用的。如果拖动它到JSP页面中，以下代码将被生成并将在页面中显示产品名称：<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width="98%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<P>&lt;netui:label value="{pageFlow.productName}"&gt;&lt;/netui:label&gt;<BR></P></TD></TR></TBODY></TABLE>
<P>试着添加其它属性到页面流中并拖动它们到JSP页面中，从而生成可以显示数据的NetUI标签。</P>
<P><BR>如果页面上有动作表单，它上面的属性也可以被拖动到JSP上。</P>
<P><BR><B>结论</B><BR>本文覆盖的范围很广。现在重新回顾一下，表达式语言使得NetUI JSP能够引用业务对象暴露的数据、页面流、以及数据绑定上下文暴露的其它对象和资源。表达式可以引用这些对象的属性和字段，并且NetUI HTML标签既可以用于显示数据，也可以用于从Web浏览器更新数据。</P>
<P>本文附带的应用程序包含多个示例，其中包括基本数据绑定规则的例子、Hello World例子、和一个创建、显示、和编辑Customer对象的复杂例子。</P>
<P></P>
<P>在以后的文章中我们还会介绍更多的内容，希望本文使您对WebLogic Workshop 8.1的从UI到数据的绑定有了初步了解。</P><img src ="http://www.blogjava.net/kapok/aggbug/4975.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-20 10:13 <a href="http://www.blogjava.net/kapok/archive/2005/05/20/4975.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLogic Portal 8.1 中的 URL</title><link>http://www.blogjava.net/kapok/archive/2005/05/19/4882.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 19 May 2005 11:28:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/19/4882.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4882.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/19/4882.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4882.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4882.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/webplat/2005051905.html">http://dev2dev.bea.com.cn/techdoc/webplat/2005051905.html</A><BR><BR>　BEA WebLogic Portal 8.1? 框架的核心任务之一是创建一致且正确的 URL ， 以便用户在门户页面环境中与各种 portlet 进行 交互。撰写本文的目的是为了向读者介绍 WebLogic Portal 中 URL 创建过程的基础结构。本文介绍 WebLogic Portal 如何创建 URL ， 可以创建的各种 URL ， URL 背 后的 API 和 层 ， 以及使用 Web Services for Remote Portlets （ WSRP ） 提供或消费 portlet 时 URL 将会发生什么变化。在介绍 URL 创建过程的同时，我还将重点强调一些最佳实践。阅读本文之后，您将有能力避免大多数常见的 URL 缺陷，并且可以利用 portlet 提供一致的用户体验。 
<P><STRONG>问题</STRONG></P>
<P>　　为什么创建 URL 是 WebLogic Portal 的核心任务 ？ 为什么 portlet 不能像在 HTML/JSP 页面中创建 URL 那样在以下示例代码中创建 URL ？ </P><PRE class=code>　　Click &lt;a href="next.jsp"&gt;here&lt;/a&gt; to continue.</PRE>
<P>　　当用户点击与此类似的链接时 ， 下面的某种情况将发生 ： </P>
<UL>
<P>
<LI>用户将看到未经门户页面聚合的 next.jsp 内容。 
<P></P>
<P></P>
<LI>用户将看到错误页面，带有可怕的异常堆栈跟踪。 
<P></P>
<P></P>
<LI>如果门户调用 portlet 使用的是 WSRP ，但是用户不具有与 WSRP Producer 直接交互的权限，则该用户的浏览器上将显示一个 DNS 或安全性错误。 </LI></UL>
<P>　　要避免这些情况的发生 ， portlet 必须要求 WebLogic Portal 创建 URL 。为什么 portlet 将此任务委派给 WebLogic Portal ？ 原因如下 ：<STRONG>&nbsp; </STRONG></P>
<P><STRONG>提示 </STRONG><STRONG>：</STRONG>在创建 portlet 时 ， 不要假定关于 URL 结构的任何事情。实际结构不仅可以由部署在 portlet 的 Web 应用程序中的模板确定，而且也可以由客户确定，比如 WSRP 。 </P>
<UL>
<P>
<LI><STRONG>门户是访问 portlet </STRONG><STRONG>的网关 —— </STRONG>当用户与 portlet 交互（即提交表单或点击链接）时，门户接收用户请求，查找负责处理该请求的 portlet ，然后调用 portlet 。门户还可提供附加服务，如个性化、权利等。因此， portlet 创建的链接必须指向门户，而不是指向 portlet 的 JSP 、 servlet 或页面流。 
<P></P>
<P></P>
<LI><STRONG>Portlet </STRONG><STRONG>从门户中去耦 </STRONG>——Portlet 是可重用单元，可以将其聚合到多个门户和桌面。因此， Portlet 不能对门户页面的 URL 假定太多。聚合 portlet 的门户可以位于： (a) 本地，在同一 Web 应用程序中， (b) 在同一服务器上的不同 Web 应用程序中，或者 (c) 物理远程位置。在 (b) 和 (c) 两种情况下，门户可以使用 WSRP 在页面中聚合 portlet 。实际上，可以同时将同一 portlet 聚合到这三种类型的门户页面中。 
<P></P>
<P></P>
<LI><STRONG>Portlet </STRONG><STRONG>不知道部署 —— </STRONG>更重要的是， portlet 对于如何部署它们无法假定太多。部署细节包括代理服务器、域名称、端口，等等。 Portlet 对这些无法做出任何假定 。 
<P></P></LI></UL>
<P>　　当您将 URL 的创建委托给 WebLogic Portal 时 ， 它将对 portlet 屏蔽门户页面 URL 、域名称、代理服务器等。门户管理员和部署人员可以控制细节，无需破坏 portlet 中的任何 URL 。 </P>
<P>　　如果您非常想知道上述链接的创建方式（假定您要在简单 JSP portlet 中创建这一链接），下面就是一个例子： </P>
<P>　　Click &lt;a href="&lt;render:jspContentUrl contentUri="next.jsp"/&gt;"&gt;here&lt;/a&gt; to continue . </P>
<P>　　现在 ， 我们来检查 WebLogic Portal 的 URL 的基础结构 </P>
<P><STRONG>URL模板</STRONG></P>
<P>　　WebLogic Portal 使用称为 “ URL 模板 ” 的简短 URL 片断创建 URL 。它们不是有效的 URL ， 需要进一步处理才能使用。要查看 URL 模板，需要创建门户项目并检查其 WEB-INF 目录。在此目录中，您将发现一个 url-templates-config.xml 文件。该文件中包含大量的默认 URL 模板。每个 URL 模板的外观如下所示： </P><PRE class=code>&lt;url-template name="default"&gt;
     {url:scheme}://{url:domain}:{url:port}/{url:path}?{url:queryString}{url:currentPage}
&lt;/url-template&gt; to continue.</PRE>
<P><STRONG>提示 ： </STRONG>您可以使用已知值替换任何可选标志。例如 ， 如果您认为永远不会通过 HTTP 访问门户页面 ， 则可以在所有 URL 模板中使用 “ http ” 替换“url:scheme” 。 </P>
<P>　　您将发现该文件不只是列出一个模板 ， 而是列出多个模板。在讨论为何需要多个模板之前，我们先看一看 URL 模板的结构。</P>
<P><STRONG>剖析URL模板</STRONG></P>
<P>　　URL 模板是带有一个或多个标志的 URL 。这些标志由 WebLogic Portal 预定义，在使用远程 portlet 的情况下，是 WSRP 1.0 规范规定的。所有标志均由大括号包围 ( { , } ) 。使用 URL 模板创建 URL 时， WebLogic Portal 使用相应的值替代这些标志，这些值或者由 URL API 或者由 JSP 标记指定，或者从部署门户的方式自动派生。 </P>
<P>　　每个 URL 模板中可以具有一个或多个下面的标志 ：</P>
<UL>
<P>
<LI>url:scheme: 这是一个可选的标志，用于指定访问该 URL 所需的 HTTP 方案。该值可以是“ http ”或“ https ”。如果在使用此 URL 模板时没有为该标志指定值， WebLogic Portal 将使用可用于生成该模板在其中发挥使用的页面的任一方案。例如，如果使用“ https ”访问包含具有模板的 URL 的页面，则它会将这一标志替换为“ https ”。 
<P></P>
<P></P>
<LI>url:domain : 这是一个可选标志 ， 用于指定访问该门户时使用的域的名称。在大多数情况下，它就是在 WebLogic Portal 之前部署的代理服务器的域名称。如果在使用 URL 模板时没有为该标志指定值， WebLogic Portal 将使用传入的 HttpServletRequest 的 getServerName() 的值。 
<P></P>
<P></P>
<LI>url:port : 这是一个可选标志 ， 用于指定访问门户时使用的端口号。在大多数情况下，它将是代理服务器的端口号。如果未指定值，则 WebLogic Portal 将使用传入的 HttpServletRequest 的 getServerPort() 的值。 
<P></P>
<P></P>
<LI>url:securePort : 这是一个可选标志 ， 用于指定访问门户时使用的 SSL 端口号。在大多数情况下，它将是代理服务器的 SSL 端口号。如果未指定值，则 WebLogic Portal 将从服务器 / 代理配置获取值。 
<P></P>
<P></P>
<LI>url:prefix: 这是一个可选标志，如果您在代理服务器设置中使用路径前缀，则需指定该标志 。 
<P></P>
<P align=left></P>
<LI>url:path : 所有 URL 模板都必须包括此标志。 WebLogic Portal 根据页面是门户的一部分还是桌面的一部分 ， 使用相应的值替换它。 
<P></P>
<P align=left></P>
<LI>url:contextualPath : 此标志是可选的 ， 应用于指向与 Web 应用程序上下文根相关的路径的 URL 。一个示例是到与 Web 应用程序上下文根相关的图像或文件的链接。 
<P></P>
<P align=left></P>
<LI>url:queryString : 所有 URL 模板都必须包括此标志。 WebLogic Portal 使用很多保留查询参数 ， 使用该标志替换那些查询参数。实际替换的参数取决于如何和在何处使用这一模板。要查找有关这些保留查询参数的更多信息，请参考 WebLogic Portal 框架中的 <A href="http://e-docs.bea.com/wlp/docs81/whitepapers/pdf/netix.pdf" target=_blank>白皮书 </A>。 
<P></P>
<P align=left></P>
<LI>url:currentPage : 这是一个可选项 ， 用于在生成的 URL 中嵌入当前的页面标签。 </LI></UL>
<P><STRONG>提示 </STRONG><STRONG>： </STRONG>记住使用用于设置代理服务器的路径前缀替换url:prefix标志。 </P>
<P>　　创建门户项目时创建的 url-templates-config.xml 文件中包含的模板列表 ， 可以满足大多数常见开发需求。</P>
<P><STRONG>URL的类型 </STRONG></P>
<P>　　WebLogic Portal 使用三种 URL </P>
<UL>
<P>
<LI>操作 URL ： 这些 URL 可用于提交用户互操作 （ 如表单提交和查询链接 ）， 可以由任何 portlet 创建。 
<P></P>
<P></P>
<LI>资源 URL ： 资源 URL 可用于生成到静态内容 （ 如图像、样式表、 JavaScript 文件、 pdf 文件等 ） 的链接。任何 portlet 都可以创建这些 URL 。 
<P></P>
<P></P>
<LI>呈现 URL 。呈现 URL 是一种特殊类型的 URL ， 供 Java （ 符合 JSR168 的 ） portlet 和远程 （ 使用 WSRP 的 ） portlet 使用。 
<P></P></LI></UL>
<P>　　稍后我们将讨论在 JSP 中如何创建这些类型的 URL 。 </P>
<P>　　参照前面的 url-templates-config.xml 文件 ， 您将发现该文件具有两个主要部分。第一部分定义大量模板。 </P>
<P>　　第二部分定义 Java 和 Page Flow portlet 如何使用这些模板。 </P>
<P>　　文件中定义的每个 url-template 具有一个名称和一个字符串值。任何两个 URL 模板的名称都不能相同。 </P>
<P>　　字符串值是带有一个或多个标志的 URL 模板字符串。 </P>
<P>　　此文件的第二部分为 Java 和 / 或 Page Flow portlet 定义一个或多个 url-template-ref 元素。使用这些元素可以指定要用于给定 URL 的 URL 模板。例如 ， 以下片断指定 ， 名为 jpf-action 的 URL 模板可用于创建在页面流中使用的操作 URL ， 而名为 jpf-secure-action 的 URL 模板 可用于在页面流中创建安全操作 URL ： </P><PRE class=code> &lt;jpf-url-templates&gt;
   &lt;url-template-ref type="action" name="jpf-action"/&gt;
   &lt;url-template-ref type="secure-action" name="jpf-secure-action"/&gt;
   ...
&lt;/jpf-url-templates&gt;</PRE>
<P><STRONG>使用URL模板</STRONG></P>
<P>　　WebLogic Portal 提供了很多使用在前一节中定义的 URL 模板的方法。 </P>
<P><STRONG>显式使用URL模板 </STRONG></P>
<P>　　利用 URL 模板 ， 您可以使用 <A href="http://edocs.beasys.com/wlp/docs81/javadoc/com/bea/portlet/GenericURL.html">com.bea.portlet.GenericURL </A>及其子类创建 URL 。下面是一个示例 ：</P><PRE class=code> &lt;% 
    ResourceURL url = ResourceURL.createResourceURL(request, response);
    url.setScheme("http");
    url.setDomain("my.domain.com");
    url.setPort(8001);
    url.setPathPrefix("/static");
    url.setTemplate("my-resource-template");
    url.setPath("/images/my.jpg");
%&gt;
&lt;img src="&lt;%=url.toString()%&gt;"/&gt;  </PRE>
<P>　　此片断使用 my-resource-template 创建到图像的链接。执行时 ， 该 JSP 生成以下代码 ： </P>
<P>&lt;img src="http://my.domain.com:8001/static/images/my.jpg"/&gt; </P>
<P><STRONG>提示 </STRONG><STRONG>： </STRONG>确保正在使用的 URL 模板具有与 setter 方法相对应的标志。如果没有 ， WebLogic Portal 将忽略到 setter 的调用。 </P>
<P>　　注意 ， 模板必须存在于 my-resource-template 文件中 ， 此片断才能生效。模板还必须具有一个指定的 <A href="http://dev2dev.bea.com.cn/techdoc/webplat/prefix">url:prefix </A>标志 ， setPathPrefix() 方法将替换 该标志。 </P>在 <A href="http://edocs.beasys.com/wlp/docs81/javadoc/com/bea/portlet/GenericURL.html" target=_blank>com.bea.portlet.GenericURL </A>或其子类的位置 ， 您可以使用在呈现标记库中可用的 URL 标记创建 URL 来代替。例如 ， 可以使用 renderUrl JSP 标记来重写上述 JSP 片断 ： <PRE class=code>&lt;%@ taglib uri="render.tld" prefix="render" %&gt;
&lt;img src="&lt;render:resourceUrl port="8001" template="default-complete" 
      scheme="http" domain="my.domain.com" 
      pathPrefix="/static" path="/images/my.jpg"/&gt; "/&gt;</PRE>
<P>　　注意 ， 所有的框架主干 JSP 均使用呈现标记创建到窗口模式 / 状态按钮的链接、到页面的链接 ， 等等。 </P>
<P><STRONG>在Portlet中使用URL模板</STRONG> </P>
<P>　　使用 Page Flow 和 Struts 标记在 Page Flow 和 Struts portlet 中创建表单、链接等时 ， 这些 JSP 标记通过 URL 重写程序将 URL 创建委派给 WebLogic Portal 。这些 URL 重写程序使用 jpf-url-templates 指定的 URL 模板创建 URL 。 </P>
<P>　　WebLogic Portal 中的 portlet 容器使用类似的方法处理 Java portlet 。创建 javax.portlet.PortletURL 时， portlet 容器使用在 url-templates-config.xml 文件中指定的 java-portlet-url-templates 。下面是一个示例：</P><PRE class=code>&lt;%
   PortletURL actionURL = renderResponse.createActionURL();
   PortletURL renderURL = renderResponse.createRenderURL();
%&gt;</PRE>
<P>　　WebLogic Portal 将分别使用 “ 操作 ” 和 “ 资源 ” 类型的 URL 模板生成这两个 URL 的字符串值。 </P>
<P><STRONG>远程Portlet中的URL</STRONG></P>
<P>　　当您使用WSRP消费远程portlet时，确保远程portlet返回的标记引用门户至关重要（WSRP Consumer）。使用远程portlet时，Consumer充当最终用户和WSRP Producer上部署的portlet的中间件。一般情况下，Producer可能设置安全性策略，限制最终用户对部署在Producer上的资源的访问权限。所以，未使用WebLogic Portal创建的链接在远程portlet上可能失败。</P>
<P>　　WSRP 1.0规范为URL创建指定了两种方法：“producer writing”和“consumer rewriting”。下面简要介绍一下这两种方法。</P>
<P><STRONG>Producer Writing</STRONG></P>
<P>　　此方法包括以下步骤：</P>
<UL>
<P>
<LI>Consumer 将一组 URL 模板发送给 Producer 。这些 URL 模板在结构上类似于上面讨论的模板 ， 不同的是它们包含由 WSRP 1.0 规范指定的标记。 
<P></P>
<P></P>
<LI>Producer 使用这些 URL 模板创建 URL ， 并向 Consumer 返回标记。 
<P></P>
<P></P>
<LI>如果需要 ， Consumer 可以进一步处理 URL 以便 URL 引用 Consumer 。 
<P></P></LI></UL>
<P>　　在这三个步骤中 ， 只有第三步依赖于 WSRP Consumer 的实现方式。如果 WebLogic Portal 就是 Consumer ， 则 Producer 的 URL 的编写包括以下步骤 ： </P>
<UL>
<P align=left>
<LI>WebLogic Portal 使用一组用于远程 portlet 的 URL 模板。这些模板连同其他 URL 模板一起在 url-templates-config.xml 文件中声明。门户 Web 应用程序的 WEB-INF 目录中的 wsrp-producer-registry.xml 文件指定了必须用于远程 portlet 的 URL 模板的名称。注意这些模板中出现的额外标志。例如， wsrp-default 模板具有类似 {wsrp-interactionState} 、 {wsrp-navigationalState} 和 {wsrp-mode} 的标志。 WSRP 1.0 规范描述这些标志的意义。 
<P></P>
<P align=left></P>
<LI>Producer 使用这些模板创建 URL 。注意 ， Producer 不一定是 WebLogic Portal 。使用其他实现也可以处理这些 URL 模板 ， 因为这些标志是由 WSRP 1.0 规范定义的。 
<P></P>
<P align=left></P>
<LI>WebLogic Portal 聚合 Producer 返回的标记而不进一步处理。 
<P></P>
<P></P>
<LI>将 WebLogic Portal 设置为 Producer 时 ， 它将使用 URL 模板 ， 这一点与本地使用 portlet 的 情况类似。惟一的区别是 ， 它使用 Consumer 发送的模板 ， 而不使用在 Producer 本地部署的模板。 
<P></P></LI></UL>
<P>　　下面是一个示例。我们假定您使用以下 JSP 部署 Page Flow portlet ： </P><PRE class=code> &lt;%@ taglib uri="netui-tags-html.tld" prefix="netui"%&gt;
 &lt;netui:anchor action="next"&gt;Next&lt;/netui:anchor&gt;</PRE>
<P>当您在 WebLogic Portal Consumer 中消费此 portlet 时 ， 它将发送以下 SOAP 请求以获取该 portlet 的标记 ： </P><PRE class=code>&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  &lt;soapenv:Body&gt;
    &lt;urn:getMarkup xmlns:urn="urn:oasis:names:tc:wsrp:v1:types"&gt;
      &lt;urn:portletContext&gt;
        &lt;urn:portletHandle&gt;portlet_1&lt;/urn:portletHandle&gt;
      &lt;/urn:portletContext&gt;
      &lt;urn:runtimeContext&gt;
        &lt;urn:userAuthentication&gt;wsrp:none&lt;/urn:userAuthentication&gt;
        &lt;urn:portletInstanceKey&gt;portlet_1_2_1&lt;/urn:portletInstanceKey&gt;
        &lt;urn:namespacePrefix&gt;portlet_1_2_1&lt;/urn:namespacePrefix&gt;
        &lt;urn:templates&gt;
          &lt;urn:defaultTemplate&gt;
            http://localhost:7001/consumer/hello.portal?_nfpb=true&amp;amp;
            _windowLabel=portlet_1_2_1&amp;amp;_pageLabel=hello_page_2&amp;amp;
            wsrp-urlType={wsrp-urlType}&amp;amp;wsrp-url={wsrp-url}&amp;amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;amp;
            wsrp-interactionState={wsrp-interactionState}&amp;amp;
            wsrp-mode={wsrp-mode}&amp;amp;wsrp-windowState={wsrp-windowState}
          &lt;/urn:defaultTemplate&gt;
          &lt;urn:blockingActionTemplate&gt;
            http://localhost:7001/consumer/hello.portal?_nfpb=true&amp;amp;
            _windowLabel=portlet_1_2_1&amp;amp;_pageLabel=hello_page_2&amp;amp;
            wsrp-urlType=blockingAction&amp;amp;wsrp-url={wsrp-url}&amp;amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;amp;
            wsrp-interactionState={wsrp-interactionState}&amp;amp;
            wsrp-mode={wsrp-mode}&amp;amp;wsrp-windowState={wsrp-windowState}
          &lt;/urn:blockingActionTemplate&gt;
          &lt;urn:renderTemplate&gt;
            http://localhost:7001/consumer/hello.portal?_nfpb=true&amp;amp;
            _windowLabel=portlet_1_2_1&amp;amp;_pageLabel=hello_page_2&amp;amp;
            wsrp-urlType=render&amp;amp;wsrp-url={wsrp-url}&amp;amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;amp;
            wsrp-interactionState={wsrp-interactionState}&amp;amp;
            wsrp-mode={wsrp-mode}&amp;amp;wsrp-windowState={wsrp-windowState}
          &lt;/urn:renderTemplate&gt;
          &lt;urn:resourceTemplate&gt;
            http://localhost:7001/consumer/resource?_windowLabel=portlet_1_2_1&amp;amp;
            wsrp-urlType=resource&amp;amp;wsrp-url={wsrp-url}&amp;amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}
          &lt;/urn:resourceTemplate&gt;
          &lt;urn:secureDefaultTemplate&gt;
            http://localhost:7002/consumer/hello.portal?_nfpb=true&amp;amp;
            _windowLabel=portlet_1_2_1&amp;amp;_pageLabel=hello_page_2&amp;amp;
            wsrp-urlType={wsrp-urlType}&amp;amp;wsrp-url={wsrp-url}&amp;amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;amp;
            wsrp-interactionState={wsrp-interactionState}&amp;amp;
            wsrp-mode={wsrp-mode}&amp;amp;wsrp-windowState={wsrp-windowState}
          &lt;/urn:secureDefaultTemplate&gt;
          &lt;urn:secureBlockingActionTemplate&gt;&lt;![CDATA[
            https://localhost:7002/consumer/hello.portal?_nfpb=true&amp;
            _windowLabel=portlet_1_2_1&amp;_pageLabel=hello_page_2&amp;
            wsrp-urlType=blockingAction&amp;wsrp-secureURL=true&amp;
            wsrp-url={wsrp-url}&amp;wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;
            wsrp-interactionState={wsrp-interactionState}&amp;
            wsrp-mode={wsrp-mode}&amp;wsrp-windowState={wsrp-windowState}]]&gt;
          &lt;/urn:secureBlockingActionTemplate&gt;
          &lt;urn:secureRenderTemplate&gt;&lt;![CDATA[
            https://localhost:7002/consumer/hello.portal?_nfpb=true&amp;
            _windowLabel=portlet_1_2_1&amp;_pageLabel=hello_page_2&amp;
            wsrp-urlType=render&amp;wsrp-secureURL=true&amp;wsrp-url={wsrp-url}&amp;
            wsrp-requiresRewrite={wsrp-requiresRewrite}&amp;
            wsrp-navigationalState={wsrp-navigationalState}&amp;
            wsrp-interactionState={wsrp-interactionState}&amp;
            wsrp-mode={wsrp-mode}&amp;wsrp-windowState={wsrp-windowState}]]&gt;
          &lt;/urn:secureRenderTemplate&gt;
          &lt;urn:secureResourceTemplate&gt;
            https://localhost:7002/consumer/resource?_windowLabel=portlet_1_2_1&amp;amp;
            wsrp-urlType=resource&amp;amp;wsrp-secureURL=true&amp;amp;
            wsrp-url={wsrp-url}&amp;amp;wsrp-requiresRewrite={wsrp-requiresRewrite}
          &lt;/urn:secureResourceTemplate&gt;
        &lt;/urn:templates&gt;
      &lt;urn:runtimeContext&gt;
      &lt;urn:userContext xsi:nil="true" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/&gt;
      &lt;urn:markupParams&gt;
        &lt;urn:secureClientCommunication&gt;false&lt;/urn:secureClientCommunication&gt;
        &lt;urn:locales&gt;en-US&lt;/urn:locales&gt;
        &lt;urn:mimeTypes&gt;text/html&lt;/urn:mimeTypes&gt;
        &lt;urn:mimeTypes&gt;*/*&lt;/urn:mimeTypes&gt;
        &lt;urn:mode&gt;wsrp:view&lt;/urn:mode&gt;
        &lt;urn:windowState&gt;wsrp:normal&lt;/urn:windowState&gt;
        &lt;urn:clientData&gt;
          &lt;urn:userAgent&gt;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; 
             SV1; .NET CLR 1.1.4322; FDM)&lt;/urn:userAgent&gt;
        &lt;/urn:clientData&gt;
      &lt;urn:markupCharacterSets&gt;UTF-8&lt;/urn:markupCharacterSets&gt;
    &lt;/urn:markupParams&gt;
  &lt;/urn:getMarkup&gt;
&lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;</PRE>
<P>　　在此请求中 ， 要注意的主要细节是模板元素。将 URL 模板发送到任何 Producer 之前 ， WebLogic Portal Consumer 会将 URL 模板中的所有 Consumer 特定的标志替换掉 ， 仅留下 WSRP 1.0 特定的标志。替换 WSRP 1.0 特定的标志由 Producer 来完成。注意，出于可读性方面的考虑，上述消息中的模板的多个行被强制换行。 </P><PRE class=code>&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  &lt;soapenv:Body&gt;
    &lt;urn:getMarkupResponse xmlns:urn="urn:oasis:names:tc:wsrp:v1:types"&gt;
      &lt;urn:markupContext&gt;
        &lt;urn:mimeType&gt;text/html; charset=UTF-8&lt;/urn:mimeType&gt;
        &lt;urn:markupString&gt;&lt;![CDATA[
          &lt;a href="http://localhost:7001/consumer/hello.portal?_nfpb=true&amp;
              _windowLabel=portlet_1_2_1&amp;_pageLabel=hello_page_2&amp;
              wsrp-urlType=blockingAction&amp;wsrp-url=&amp;wsrp-requiresRewrite=&amp;
              wsrp-navigationalState=&amp;
              wsrp-interactionState=_action%3D%252Fhello%252FHelloWorld%252Fbegin&amp;
              wsrp-mode=&amp;wsrp-windowState="&gt;Next&lt;/a&gt;
     ]]&gt;&lt;/urn:markupString&gt;
        &lt;urn:locale&gt;en-US&lt;/urn:locale&gt;
        &lt;urn:requiresUrlRewriting&gt;false&lt;/urn:requiresUrlRewriting&gt;
      &lt;/urn:markupContext&gt;
    &lt;/urn:getMarkupResponse&gt;
  &lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;</PRE>
<P><STRONG>提示 </STRONG><STRONG>： </STRONG>WebLogic Portal Producer 可以将 URL 模板存储在 HTTP 会话中 ， 所以 ， WebLogic Portal Consumer 每个会话发送一次 URL 模板。 </P>
<P>　　注意 ， Producer 使用 blockingActionTemplate 创建 netui:anchor 标记的链接。由于在这次响应中返回的 portlet 标记完全合格 ， 不需要在 Consumer 上对其进行进一步地处理。 </P>
<P>　　如果您有意了解关于这些 SOAP 消息内容的更多信息 ， 请访问 OASIS WSRP TC <A href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp" target=_blank>主页 </A>， 并下载最新版本的 WSRP 1.0 Primer 。</P>
<P><STRONG>Consumer Rewriting </STRONG></P>
<P>　　在此方法中 ， Consumer 不发送 URL 模板 ， 而是执行以下步骤 ： </P>
<P>　　Consumer 为 portlet 标记发出请求。该请求不包括 URL 模板。 </P>
<P>　　Producer 创建链接 ， 这些链接被封装在由 WSRP 1.0 规范指定的特殊标记中。 </P>
<P>　　Consumer 分析该标记 ， 并使用适当的 URL 替换这些标记中的链接。 </P>
<P>　　对于上述示例 portlet ， Producer 返回如下响应 ： </P><PRE class=code>&lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;
  &lt;soapenv:Body&gt;
    &lt;urn:getMarkupResponse xmlns:urn="urn:oasis:names:tc:wsrp:v1:types"&gt;
      &lt;urn:markupContext&gt;
        &lt;urn:mimeType&gt;text/html; charset=UTF-8&lt;/urn:mimeType&gt;
        &lt;urn:markupString&gt;Hello World 
           &amp;lt;a href="wsrp_rewrite?wsrp-urlType=blockingAction&amp;wsrp-secureURL=false
           &amp;wsrp-interactionState=_action%3D%252Fhello%252FHelloWorld%252Fbegin/wsrp_rewrite"&gt;Begin
           &amp;lt;/a&gt;&lt;/urn:markupString&gt;
        &lt;urn:locale&gt;en-US&lt;/urn:locale&gt;
        &lt;urn:requiresUrlRewriting&gt;true&lt;/urn:requiresUrlRewriting&gt;
      &lt;/urn:markupContext&gt;
    &lt;/urn:getMarkupResponse&gt;
  &lt;/soapenv:Body&gt;
&lt;/soapenv:Envelope&gt;</PRE>
<P>　　注意 ， 链接的 href 值被封装在 wsrp_rewrite? 和 /wsrp_rewrite 标记之间。然后 ， WebLogic Portal Consumer 分析这些记号中的标记 ， 并使用部署在 Consumer 上的 URL 模板创建 URL 。<STRONG>&nbsp; </STRONG></P>
<P><STRONG>提示 ： </STRONG>如果将 WebLogic Portal 用作 Consumer ， 则可以尝试对 Producer 进行设置 ， 以便更好地使用 URL 模板。</P>
<P><STRONG>Producer Writing还是Consumer Rewriting？ </STRONG></P>
<P>　　URL 创建的两种方法的主要不同点在于 URL 模板的使用。由 WebLogic Portal Consumer 发送给 Producer 的那种模板不需要对 Producer 返回的标记进行进一步地处理。因此，将 WebLogic Portal 用作 Consumer 时，此方法执行地较好。 </P>
<P>　　WebLogic Portal Producer （ 无论简单还是复杂 ） 默认的设置是使用 Producer writing 生成 URL 。您可以通过更新 Producer 的 WEB-INF 目录下的 wsrp-producer-config.xml文件改用 Consumer rewriting 。请参考 <A href="http://edocs.beasys.com/wlp/docs81/wsrp/index.html" target=_blank>关于 WSRP 的 WebLogic Portal 文档资料 </A>以获取更多细节。</P>
<P><STRONG>结束语</STRONG></P>
<P>　　在本文中 ， 我的目的之一是想指出为什么创建 URL 对于提供一致的用户体验是关键一步。当您使用 WebLogic Portal 提供的 API 和 JSP 标记、 Page Flow 或 Struts 标记、或者 Java Portlet API 或标记时， URL 创建过程依赖于 URL 模板。 WebLogic Portal 具有消费本地部署的模板和远程 Consumer 发送的模板的能力。这些功能在设计上允许 URL 模板控制所有创建的链接，以声明的方式即可调整，无需更改代码。 </P>
<P>　　反向工程生成的 URL 结构以手动创建链接 （ 即不使用任何 WebLogic Portal API 或标签 ）， 也是很可能实现的。这种链接在有些情况下可以使用，但是当部署配置变更或使用 WSRP 时，则很可能失败。下面是最新的准则。始终使用以下标签创建链接： </P>
<UL>
<P>
<LI>在框架主干 JSP 和 portlet JSP 中 ： com.bea.portlet.GenericURL 或其子类或者呈现标签库中的 URL 标签 
<P></P>
<P></P>
<LI>在 Page Flow portlet 中 ： Page Flow HTML 标记 
<P></P>
<P></P>
<LI>在 Struts portlet 中 ： Struts HTML 标记 
<P></P>
<P></P>
<LI>在 Java Portlet 中 ： Java Portlet API 或 portlet 标记库中的 URL 标记 
<P></P></LI></UL>
<P>　　您完全可以不使用这些标记 ， 而按照自己的意思创建 URL 。但这样的 URL 有时可能不可用。 </P>
<P><STRONG>参考资料 </STRONG></P>
<P><A href="http://edocs.beasys.com/wlp/docs81/wsrp/index.html" target=_blank>关于 WSRP 的 WebLogic Portal 文档资料 </A></P>
<P><A href="http://edocs.beasys.com/wlp/docs81/javadoc/com/bea/portlet/GenericURL.html" target=_blank>WebLogic Portal GenericURL 文档资料 </A></P>
<P><A href="http://e-docs.bea.com/wlp/docs81/whitepapers/pdf/netix.pdf" target=_blank>描述 WebLogic Portal Framework 的白皮书 </A></P>
<P><A href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp" target=_blank>OASIS WSRP TC 主页 </A></P>
<P><STRONG>关于作者</STRONG> </P>
<P>　　Subbu Allamaraju 是 BEA Systems 的 WebLogic Portal 开发团队的专职软件工程师。从事 WSRP 标准及其 WebLogic Portal 实现方面的工作。此外他还在 subbu.org 上扮演一个技术博客的角色。 </P>
<P><STRONG>原文出处</STRONG></P>
<P><A href="http://dev2dev.bea.com/products/wlportal81/articles/urls_in_portal.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/articles/urls_in_portal.jsp</A></P><img src ="http://www.blogjava.net/kapok/aggbug/4882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-19 19:28 <a href="http://www.blogjava.net/kapok/archive/2005/05/19/4882.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE安全系统</title><link>http://www.blogjava.net/kapok/archive/2005/05/17/4405.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 17 May 2005 03:59:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/17/4405.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4405.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/17/4405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4405.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4405.html</trackback:ping><description><![CDATA[<FONT color=#006666><A href="http://www.ebuilds.net/news/kanwu/4/4-03.htm">http://www.ebuilds.net/news/kanwu/4/4-03.htm</A><BR><BR>前言<BR></FONT>　　 J2EE(Java 2 Platform Enterprise Edition)是美国Sun公司推出的多层企业应用开发模型。J2EE简化了基于工业标准的、组件化的企业应用开发，提供了一套完整的企业应用的开发框架和服务(Security、Transaction、Naming等)的支持。因此，越来越多的企业应用构建在J2EE平台上。在企业应用中，首要关心的是应用的安全性，本文首先介绍J2EE中的安全概念和J2EE的安全体系架构。最后展示在程序的开发中怎样应用J2EE提供的安全特性。<BR><BR><FONT color=#006666>J2EE中的安全概念</FONT> <BR>　　 通俗的讲，一个安全的系统首先是保证通过了系统验证的用户才能进入系统，不通过系统验证的用户是不能进入系统的。通过系统验证的用户只能在授权的范围内操作，而不能接触到任何未经授权的任何内容（包括功能和数据）。为此目的，在系统中需要先定义以下几种基本的安全概念：<BR>　　主体（Principal）：主体（Principal）是被在企业安全服务验证了的实体。主体（Principal）用主体名作为它的标识，通过与主体相关的验证数据进行验证。通常情况下主体名就是用户的登陆名，验证数据就是登陆的密码。 J2EE规范中并没有限定J2EE 产品提供商使用怎样的认证方法，因此主体名和验证数据的内容和格式依不同的认证协议而不同。<BR>　　安全策略域（Security Policy Domain）：也称安全域（security domain）或 realm，它是一个逻辑范围或区域，在这一范围或区域中安全服务的管理员定义和实施通用的安全策略。它是从安全策略的角度划分的区域。比如可以将企业应用系统划分为企业员工、供应商、合作伙伴等不同的安全域，对这些安全区域采用不同的安全策略。<BR>　　安全技术域（Security Technology Domain）：它是从安全技术的角度划分的区域，在一个安全技术域中使用同样的安全机制来执行安全策略。一个安全技术域可以包括多个安全策略域。<BR>　　安全属性（Security Attributes）：每个主体（Principal）都有一系列与之相关的安全属性。安全属性可用来访问被保护的资源，检查用户的身份和完成其他一些安全相关的用途。J2EE产品提供商或具体的验证服务的实现来决定怎样将安全属性与一个主体联系起来。J2EE规范并没有限定什么样的安全属性将与主体相联系。<BR>　　信任域（Credential）：信任域包含或引用为J2EE系统验证一个主体的验证信息（安全属性）。如果成功的通过了验证，主体将获得一个包括安全属性的信任域。如果被允许的话，一个主体也可能获取另一个主体的信任域。在这种情况下两个主体在同一安全域中具有相同的安全属性。<BR><BR><FONT color=#006699><FONT color=#006666>J2EE安全体系<BR></FONT></FONT>　　1、基于容器的安全<BR>　　在J2EE的环境中，组件的安全是由他们各自的容器来负责的，组件的开发人员几乎可以不用或者很少在组件中添加有关安全的代码。这种安全逻辑和业务逻辑相对独立的架构，使得企业级应用系统有更好的灵活性和扩展性。J2EE规范要求J2EE 产品必须为应用程序开发者提供两种形式的基于容器的安全性：说明性的安全性和可编程的安全性。<BR>　　I、说明性的安全性<BR>　　说明性的安全性通过安全结构描述的方式来代表应用程序的安全需求，安全结构一般包括安全角色，访问控制和验证要求等。在J2EE平台中部署描述符充当了说明的安全性的主要工具。部署描述符是组件开发者和应用程序部署者或应用程序组装者之间的交流工具。应用程序的开发者用它来表示应用中的安全需求，应用程序部署者或应用程序组装者将安全角色与部署环境中的用户和组映射起来。<BR>　　在程序运行时容器从部署描述符中提取出相应的安全策略，然后容器根据安全策略执行安全验证。说明的安全性不需要开发人员编写任何安全相关的代码，一切都是通过配置部署描述符来完成的。<BR>　　II、可编程的安全性<BR>　　可编程的安全性在说明性的安全性的基础上，使安全敏感的应用可以通过调用容器提供的API来对安全作出决断。这在说明性的安全性不足以满足企业的安全模型的情况是非常有用的。J2EE在EJB EjbConext interface和servlet HttpServletRequest interface中各提供两个方法：<BR>　　isCallerInRole (EJBContext)<BR>　　getCallerPrincipal (EJBContext)<BR>　　isUserInRole (HttpServletRequest)<BR>　　GetUserPrincipal (HttpServletRequest) <BR>　　这些方法允许组件根据调用者或远程用户的安全角色来作出商业判断。<BR>　　2、J2EE的验证模型<BR>　　身份验证是用户或组件调用者向系统证明其身份的过程。用户通过某种方式向系统提交验证信息（通常是用户名和密码或者是用户的数字证书），系统以用户提供的验证信息和系统的安全策略来验证用户的身份。 
<TABLE class=unnamed2 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD align=middle><IMG height=132 src="http://www.ebuilds.net/news/kanwu/4/images/4-1.gif" width=336></TD></TR>
<TR>
<TD align=middle>图一 初始验证过程</TD></TR>
<TR>
<TD align=middle><IMG height=170 src="http://www.ebuilds.net/news/kanwu/4/images/4-2.gif" width=471></TD></TR>
<TR>
<TD align=middle>图二 验证URL</TD></TR>
<TR>
<TD align=middle><IMG height=145 src="http://www.ebuilds.net/news/kanwu/4/images/4-3.gif" width=392></TD></TR>
<TR>
<TD align=middle>图三 验证EJB方法调用</TD></TR></TBODY></TABLE>　　I、Web 客户端的验证<BR>　　Web客户端通常通过http协议来请求web服务器端的资源，这些web资源通常包括html网页、jsp（java server page）文件、java servlet和其他一些二进制或多媒体文件。在企业环境中，企业的某些资源往往要求只允许某些人访问，有些资源甚至是机密的或安全敏感的。因此对企业中各种web资源进行访问控制是十分必要的。为了满足企业中的不同安全级别和客户化的需求，J2EE提供了三种基于web客户端的验证方式：<BR>　　<FONT color=#999999>●</FONT>HTTP基本验证（HTTP Basic Authentication）<BR>　　<FONT color=#999999>●</FONT>基于表单的验证（Form-Based Authentication）<BR>　　<FONT color=#999999>●</FONT>基于客户端证书的验证（Client-Certificate Authentication） <BR>　　II、应用程序客户端的验证<BR>　　Java客户端程序是执行在用户本地java虚拟机上的java程序，它拥有main方法，通常用户可通过java.exe或javaw.exe直接启动执行。J2EE应用程序客户端与java客户端程序相似，也拥有main方法，但他们在运行时存在一定的差别。J2EE应用程序客户端和其他J2EE组件一样运行在自己的容器中。用户通过容器来执行J2EE应用程序客户端。这样J2EE应用程序客户端容器就有机会在J2EE应用程序客户端被执行之前完成用户身份的验证。J2EE提供了一种可自定义的方式来获取用户的验证信息。可以选择使用容器提供的缺省的方式来获取J2EE应用客户端程序的用户的验证信息，也可以选择自定义的方式来获取用户的验证信息。<BR>　　3、J2EE的授权模型<BR>　　I、代码授权（Code Authorization）<BR>　　J2EE产品通过java 2 安全模型来限制特定J2SE的类和方法的执行，以保护和确保操作系统的安全。<BR>　　II、调用者授权（Caller Authorization）<BR>　　安全角色：安全角色是具有相同安全属性的逻辑组。它由应用程序的装配者（Application Assembler）或应用程序的部署者（Application Deployer）分配的。<BR>　　III、安全角色引用<BR>　　安全角色引用是应用程序提供者（Application Provider）用来引用安全角色的标识。应用程序提供者（Application Provider）可以用安全角色引用来为安全角色分配资源访问的权限，也可以在安全相关的程序代码中引用安全角色。<BR>　　IV、用户和组<BR>　　用户和组是在实际系统环境下的用户和用户的集合。它们对应着现实当中的人和群体。<BR>　　V、访问控制<BR>　　访问控制可以确保安全角色只能访问已授予它安全权限的授权对象。授权对象包括EJB的远程方法、web资源（html网页，jsp/servlet和多媒体或二进制文件）等。在J2EE中访问控制在应用程序描述文件中与安全角色关联起来。<BR>　　VI、映射<BR>　　通过映射应用程序的系统管理员将实际系统环境中的用户和角色与安全角色联系起来，从而是实际的用户拥有对企业资源访问的适当授权。<BR>　　VII、被传播的调用者身份标识<BR>　　在J2EE 1.3中可以选择用传播调用者标识作为web组件和ejb组件调用者的标识来进行验证。在这种方式下，整个ejb组件的调用链中interface EJBContext的方法getCallerPrincipal返回相同的主体名（principal name）。如果调用链中的第一个ejb是被jsp/servlet调用的，interface EJBContext的方法getCallerPrincipal返回的主体名（principal name）应与interface HttpServletRequest的方法getUserPrincipal的返回值相同。要注意的是在调用链中传递的是用户的标识，而不是凭证（credentials），这一点非常重要，因为在调用链的每个节点上用户可能使用不同的安全属性。<BR>　　VIII、Run As Identities<BR>　　J2EE 1.3中提供了允许组件开发者和部署者来指定组件以什么身份运行的方法。符合J2EE1.3规范的产品会提供将组件设置成Run As Identities方式的方法。如果Run As Identities方式被选中，在运行中被设置为Run As Identities的组件的调用者不再是调用链中第一个节点的调用者了，而是在部署时被指定的调用者。而调用链中随后节点的调用者也变为与被设置为Run As Identities的组件的调用者相同。<img src ="http://www.blogjava.net/kapok/aggbug/4405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-17 11:59 <a href="http://www.blogjava.net/kapok/archive/2005/05/17/4405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>weblogic config.xml</title><link>http://www.blogjava.net/kapok/archive/2005/05/17/4401.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 17 May 2005 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/17/4401.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4401.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/17/4401.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4401.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4401.html</trackback:ping><description><![CDATA[<P>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<BR>&lt;Domain ConfigurationVersion="8.1.4.0" Name="portal"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Server AcceptBacklog="50" DefaultProtocol="t3"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultSecureProtocol="t3s" HttpdEnabled="true" ListenAddress=""<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ListenPort="7001" Name="portalServer" NativeIOEnabled="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReliableDeliveryPolicy="RMDefaultPolicy" ServerVersion="8.1.4.0"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdoutDebugEnabled="false" StdoutSeverityLevel="32"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionLogFilePrefix="./logs/" TunnelingClientPingSecs="45" TunnelingClientTimeoutSecs="40"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebServer AuthCookieEnabled="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogFileName="./logs/access.log" LoggingEnabled="true" Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;COM Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ServerStart Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;SSL Enabled="true" IdentityAndTrustLocations="KeyStores"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ListenPort="7002" Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Log FileMinSize="20000" Name="portalServer" RotationType="bySize"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;KernelDebug Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;KernelDebug Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ServerDebug Name="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ExecuteQueue Name="default" ThreadCount="15"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ExecuteQueue Name="portalRenderQueue" ThreadCount="5"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;JTAMigratableTarget Name="portalServer" UserPreferredServer="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Server&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;ApplicationManager Name="portal"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Application Deployed="true" LoadOrder="1000"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="JWSQueueTransport" Path="D:\bea\weblogic81\server\lib" TwoPhase="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="QueueTransportEJB" Targets="portalServer" URI="QueueTransportEJB.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Application&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Application Deployed="false" Name="paymentWSApp"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Path="D:\bea\weblogic81\portal\lib\paymentWSApp.ear" TwoPhase="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="payment" Targets="portalServer" URI="payment.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebServiceComponent Name="payws" Targets="portalServer" URI="pay-ws"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Application&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Application Deployed="false" Name="taxWSApp"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Path="D:\bea\weblogic81\portal\lib\taxWSApp.ear" TwoPhase="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="tax" Targets="portalServer" URI="tax.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebServiceComponent Name="taxws" Targets="portalServer" URI="tax-ws"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Application&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;FileRealm Name="wl_default_file_realm"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Realm FileRealm="wl_default_file_realm" Name="wl_default_realm"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;PasswordPolicy Name="wl_default_password_policy"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Security Name="portal" PasswordPolicy="wl_default_password_policy"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Realm="wl_default_realm" RealmSetup="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.authentication.DefaultAuthenticator<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultAuthenticator" Realm="Security:Name=myrealm"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.authentication.DefaultIdentityAsserter<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActiveTypes="AuthenticatedUser"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultIdentityAsserter" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.authorization.DefaultRoleMapper<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultRoleMapper" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.authorization.DefaultAuthorizer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultAuthorizer" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.authorization.DefaultAdjudicator<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultAdjudicator" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.security.providers.credentials.DefaultCredentialMapper<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmDefaultCredentialMapper" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.management.security.authentication.UserLockoutManager<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmUserLockoutManager" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;weblogic.management.security.Realm<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Adjudicator="Security:Name=myrealmDefaultAdjudicator"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AuthenticationProviders="Security:Name=myrealmDefaultAuthenticator|Security:Name=myrealmDefaultIdentityAsserter|Security:Name=myrealmWSRPIdentityAsserter"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Authorizers="Security:Name=myrealmDefaultAuthorizer"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CredentialMappers="Security:Name=myrealmDefaultCredentialMapper"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultRealm="true" DisplayName="myrealm"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FullyDelegateAuthorization="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealm"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RoleMappers="Security:Name=myrealmDefaultRoleMapper" UserLockoutManager="Security:Name=myrealmUserLockoutManager"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;com.bea.wsrp.security.WSRPIdentityAsserter<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActiveTypes="WSRPPerimeterAtnToken"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Base64DecodingRequired="false"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="Security:Name=myrealmWSRPIdentityAsserter" Realm="Security:Name=myrealm"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Security&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;SNMPAgent Name="portal"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Log FileMinSize="20000" FileName="./wl-domain.log" Name="portal" RotationType="bySize"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JMSConnectionFactory<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="weblogic.jws.jms.QueueConnectionFactory"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="cgQueue"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Notes="User transactions must be enabled to ensure transaction coordination for persistence and asynchronous operations"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Targets="portalServer" UserTransactionsEnabled="true"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JMSFileStore Directory="rmfilestore" Name="FileStore"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JMSJDBCStore ConnectionPool="cgJMSPool-nonXA" Name="cgJMSStore" PrefixName="weblogic"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JMSServer Name="cgJMSServer" Store="cgJMSStore" Targets="portalServer"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;JMSQueue JNDIName="jws.queue" Name="cgJWSQueue"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RedeliveryLimit="2" StoreEnabled="default"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/JMSServer&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;WSReliableDeliveryPolicy DefaultRetryCount="10"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultTimeToLive="60000" Name="RMDefaultPolicy" Store="FileStore"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCConnectionPool CapacityIncrement="1"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DriverName="com.pointbase.jdbc.jdbcUniversalDriver"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitialCapacity="5" MaxCapacity="50" Name="cgPool"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PasswordEncrypted="{3DES}s4loB2ZDe75pVWDp7VCO5Q=="<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Properties="user=weblogic" RefreshMinutes="0"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShrinkPeriodMinutes="15" ShrinkingEnabled="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SupportsLocalTransaction="true" Targets="portalServer"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestConnectionsOnRelease="false"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestConnectionsOnReserve="false" URL="jdbc:pointbase:server://localhost:9093/workshop"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCConnectionPool CapacityIncrement="1"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DriverName="com.pointbase.jdbc.jdbcUniversalDriver"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitialCapacity="5" MaxCapacity="50" Name="cgJMSPool-nonXA"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PasswordEncrypted="{3DES}s4loB2ZDe75pVWDp7VCO5Q=="<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Properties="user=weblogic" RefreshMinutes="0"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShrinkPeriodMinutes="15" ShrinkingEnabled="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SupportsLocalTransaction="true" Targets="portalServer"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestConnectionsOnRelease="false"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestConnectionsOnReserve="false" URL="jdbc:pointbase:server://localhost:9093/workshop"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCConnectionPool CapacityIncrement="1"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DriverName="com.pointbase.jdbc.jdbcUniversalDriver"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitialCapacity="5" MaxCapacity="50" Name="portalPool"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PasswordEncrypted="{3DES}s4loB2ZDe75pVWDp7VCO5Q=="<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Properties="user=weblogic" RefreshMinutes="0"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShrinkPeriodMinutes="15" ShrinkingEnabled="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Targets="portalServer" TestConnectionsOnRelease="false"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestConnectionsOnReserve="false" URL="jdbc:pointbase:server://localhost:9093/workshop"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCDataSource JNDIName="p13n.trackingDataSource"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="p13n_trackingDataSource" PoolName="cgJMSPool-nonXA" Targets="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCDataSource<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="p13n.sequencerDataSource;p13n.dataSyncDataSource;p13n.entitlementsDataSource"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="p13nDataSource" PoolName="cgJMSPool-nonXA" Targets="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCTxDataSource EnableTwoPhaseCommit="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="cgDataSource;cgSampleDataSource" Name="cgDataSource"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PoolName="cgPool" Targets="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCTxDataSource EnableTwoPhaseCommit="true"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="cgDataSource-nonXA;weblogic.jdbc.jts.ebusinessPool"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="cgDataSource-nonXA" PoolName="cgJMSPool-nonXA" Targets="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JDBCTxDataSource EnableTwoPhaseCommit="false"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="portalFrameworkPool;contentDataSource;weblogic.jdbc.jts.commercePool"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="portalFrameworkPool" PoolName="portalPool" Targets="portalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JTA Name="portal" TimeoutSeconds="500"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;EmbeddedLDAP<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CredentialEncrypted="{3DES}HMSGZ1HaJ4VJJp5iPsboY2qt7eMGGlyJSzpb0VaY60s=" Name="portal"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;SecurityConfiguration<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CredentialEncrypted="{3DES}88uPm33g3MGkMPADkzvpPzRLt1DSFb5Ua8+9I7M8VtyQ8jGORFsxHbg6J6t+Fawqv69kg2seht65KpJJiReAPGLQZh3RfJPF"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name="portal" RealmBootStrapVersion="1"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;JMSServer Name="WSStoreForwardInternalJMSServerportalServer"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Store="FileStore" Targets="portalServer"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;JMSQueue CreationTime="1115867722375"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="jms.internal.queue.WSStoreForwardQueue"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDINameReplicated="false" Name="WSInternaljms.internal.queue.WSStoreForwardQueueportalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;JMSQueue CreationTime="1115867722906"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDIName="jms.internal.queue.WSDupsEliminationHistoryQueue"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JNDINameReplicated="false" Name="WSInternaljms.internal.queue.WSDupsEliminationHistoryQueueportalServer"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/JMSServer&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;Application Name="portalApp"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Path="D:\bea\weblogic81\samples\portal\portalApp"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StagingMode="nostage" TwoPhase="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="commerce.jar" Targets="portalServer" URI="commerce.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebAppComponent Name="sampleportal" Targets="portalServer" URI="sampleportal"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebAppComponent Name="tutorial" Targets="portalServer" URI="tutorial"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebAppComponent Name="portalAppAdmin" Targets="portalServer" URI="adminPortal.war"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebServiceComponent Name="portalAppTool" Targets="portalServer" URI="wps-toolSupport.war"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebAppComponent Name="portalAppDataSync" Targets="portalServer" URI="datasync.war"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="netuix.jar" Targets="portalServer" URI="netuix.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="prefs.jar" Targets="portalServer" URI="prefs.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="p13n_ejb.jar" Targets="portalServer" URI="p13n_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="pipeline.jar" Targets="portalServer" URI="pipeline.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="wps.jar" Targets="portalServer" URI="wps.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="content_repo.jar" Targets="portalServer" URI="content_repo.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="content.jar" Targets="portalServer" URI="content.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;WebAppComponent Name="defaultWebApp" Targets="portalServer" URI="defaultWebApp"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="calendar_ejb.jar" Targets="portalServer" URI="calendar_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="contact_ejb.jar" Targets="portalServer" URI="contact_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="todo_ejb.jar" Targets="portalServer" URI="todo_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="discussion_ejb.jar" Targets="portalServer" URI="discussion_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="security_ejb.jar" Targets="portalServer" URI="security_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;EJBComponent Name="uniqueid_ejb.jar" Targets="portalServer" URI="uniqueid_ejb.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/Application&gt;<BR>&lt;/Domain&gt;<BR></P><img src ="http://www.blogjava.net/kapok/aggbug/4401.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-17 10:19 <a href="http://www.blogjava.net/kapok/archive/2005/05/17/4401.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>An Overview of Role-Based Security</title><link>http://www.blogjava.net/kapok/archive/2005/05/17/4398.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 17 May 2005 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/17/4398.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4398.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/17/4398.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4398.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4398.html</trackback:ping><description><![CDATA[<DIV id=topictitle>
<H1 class=Title>An Overview of Role-Based Security</H1></DIV>
<DIV id=topictext>
<P>The topics in this section explain how role-based security can be used to restrict access to resources (web services, page flows, Java controls, EJBs) to only those users who have been granted a particular security role. It also explains the relationship between EJB-scoped, application-scoped, web-application scoped, and global security roles.</P>
<P>To restrict access you set up two kinds of tests that candidate users must pass to access some resources: an <EM>authentication</EM> process, which determines the user's identity and group membership, and an <EM>authorization</EM> process, which decides whether a user has the role membership necessary to access a particular resource. Once a user has access to a method and the method executes, it can run under the security role of the user or under a different security role.</P>
<H2>The Authentication Process</H2>
<P>A candidate user is first tested against the authentication process. The authentication process is generally a login process, where the candidate user is asked to provide a username and password. If the candidate succeeds in passing this challenge, the user is granted a set of identities: one identity is his username identity, the other identities are the set of groups that user has membership in. The user's username identity and group identities are called the user's <EM>principals</EM>: think of these principals as a set of credentials that the user presents when he/she wants to access some resource protected by an authorization process. For more information, see <A href="javascript:reloadTOC('../authentication/ovwUsernamePasswordAuthentication.html')">Authentication</A>.</P>
<H2>The Authorization Process</H2>
<P>In the authorization process, users are tested to see if they have been granted the required role to access the protected resource. If they have been granted the required role, they can access the resource; if they haven't, they are denied access. A user has been granted a particular role if one of his/her principals has been granted a particular role. Principals are granted roles by a set of role-principal mappings. </P>
<P class=notepara><STRONG>Note</STRONG>. A user can be a person or another software component. For instance, a web service can invoke an EJB's method with security restrictions; if the web service does not pass the authorization process, it is prevented from invoking the EJB method.</P>
<H3>Global Roles</H3>
<P>Global roles are available to all resources within a server's security realm, that is, a server's domain. These roles can be used by any application and any resource using this domain. WebLogic Server predefines a set of global roles but you can define additional global roles as needed. For more information, see the WebLogic Server help topic <A href="http://edocs.bea.com/wls/docs81/secwlres/index.html" target=_blank>Securing WebLogic Resources</A>.</P>
<H3>Scoped Roles</H3>
<P>Scoped roles apply to a particular resource. WebLogic Workshop applications can have three different scopings:</P></DIV>
<OL>
<LI>Application scoped (defined in the application's application.xml / weblogic-application.xml files) 
<LI>
<DIV>Web application scoped (defined in a project's web.xml / weblogic.xml files) </DIV>
<LI>
<DIV>EJB scoped (defined in an EJB's ejb-jar.xml / weblogic-ejb-jar.xml files) </DIV></LI></OL>
<DIV>
<P>Application scoped roles can be used in an authorization process to protect any of the resources within the application, whereas web application scoped roles apply only to the resources within an individual web project and EJB scoped roles apply only to the resources within an individual EJB. For instance, if you want a security role to be defined just for a particular EJB, you make it EJB-scoped.</P>
<P>Note that EJB scoped roles do not exclusively protect WebLogic Workshop's EJB projects: they also can be used to protect Web Services, Java control extensions (JCX files), and JPD files. This is because all these files are <EM>compiled into</EM> EJBs at compile time.</P>
<P>The following diagram shows the three kinds of scoped roles, and corresponding deployment descriptors, that you can define with WebLogic Workshop.</P>
<P><IMG height=460 src="http://e-docs.bea.com/workshop/docs81/doc/en/workshop/images/role-based-security-scoped-roles.gif" width=689></P>
<P class=notepara><STRONG>Note</STRONG>. You can also define scoped security roles for other resources such as JDBC resources. For more information, see the WebLogic Server help topic <A href="http://edocs.bea.com/wls/docs81/secwlres/index.html" target=_blank>Securing WebLogic Resources</A>.</P>
<H3>Role-Principal Mapping</H3>
<P>Role-principal mappings define how principals map to security roles. A particular user can be mapped to one or more security roles or a group can be mapped to one or more security roles. Role-principal mappings for a scoped role are defined in the appropriate deployment descriptor configuration file (see the <SPAN class=langinline>&lt;security-role-assignment&gt;</SPAN> fragments in the above picture; this is discussed in more detail in <A href="javascript:reloadTOC('conRoleBasedsecurity.html')">Implementing Role-Based Security</A>). </P>
<P>For scoped roles, you can alternatively use the <SPAN class=langinline>&lt;externally-defined/&gt;</SPAN> element to indicate that the role and role-principal mapping are defined elsewhere in the security realm. Specifically, when you use this element for EJB-scoped or web application scoped roles, WebLogic Server first examines the application-scoped roles for a role with the same name and with a role-principal mapping definition. If no appropriate application-scoped roles are found, global roles are examined. For application-scoped roles with the <SPAN class=langinline>&lt;externally-defined/&gt;</SPAN> element, global roles are examined for role-principal mappings.</P>
<P class=notepara><STRONG>Note</STRONG>. When you map a scoped role to a principal, the principal is assumed to exist in the security realm. Role-principal mapping does not have the side effect of defining the principal if it doesn't exist. For more inforrmation, see <A href="javascript:reloadTOC('conTestRoleBasedSecurity.html')">Creating Principals and Role-Principal Mappings</A>. </P>
<H2>Running Under Another Security Role</H2>
<P>An EJB, Java control, or web service method can run under the security role of the invoking user, or it can run under a different security role and principal. This might for instance be necessary when the EJB or web service in turn use resources that have strict security requirements. For an example, see <A href="javascript:reloadTOC('../../../../wls/samples/samSecurity.html')">EJB Security Sample</A>.</P>
<P class=relatedtopics>Related Topics</P>
<P><A href="javascript:reloadTOC('../authentication/ovwUsernamePasswordAuthentication.html')">Authentication</A></P>
<P><A href="javascript:reloadTOC('conRoleBasedsecurity.html')">Implementing Role-Based Security</A></P>
<P><A href="javascript:reloadTOC('../../howdoi/HowCreateAppScopedSecurityRole.html')">How Do I: Create An Application-Scoped Security Role?</A></P></DIV><!-- InstanceEndEditable -->
<SCRIPT language=JavaScript>
writeTopicInfo();
</SCRIPT>

<HR SIZE=1><img src ="http://www.blogjava.net/kapok/aggbug/4398.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-17 10:04 <a href="http://www.blogjava.net/kapok/archive/2005/05/17/4398.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发安全的 Enterprise Bean </title><link>http://www.blogjava.net/kapok/archive/2005/05/16/4335.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 16 May 2005 06:11:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/16/4335.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4335.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/16/4335.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4335.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4335.html</trackback:ping><description><![CDATA[<SPAN class=chapTitle><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html">http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html</A><BR>开发安全的 Enterprise Bean</SPAN> 
<P></P><A name=24448></A>
<P>本节讲述安全管理功能在 EJB 体系架构中的工作方式，并提供针对环境开发安全的 Enterprise Bean 的准则。</P><A name=24452></A>
<P><BR>
<HR SIZE=1>

<TABLE cellPadding=5 width="75%" border=0>
<TBODY>
<TR>
<TD vAlign=top><A name=24459></A><B>注意</B> </TD>
<TD vAlign=top><A name=24461></A>
<P>如果不熟悉 EJB 技术，请参阅 Java 软件教程：</P>
<DL>
<DL><A name=24465></A>
<DT>
<P><SPAN class=monospace><A href="javascript:if(confirm('http://java.sun.com/j2ee/docs.html  \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address.  \n\nDo you want to open it from the server?'))window.location='http://java.sun.com/j2ee/docs.html'" tppabs="http://java.sun.com/j2ee/docs.html">http://java.sun.com/j2ee/docs.html</A></SPAN></P></DT></DL></DL><A name=24499></A>
<P>有关 EJB 安全性的信息，请参阅 Enterprise JavaBeans Specification 2.0 第 21 章“安全管理”。</P><A name=24508></A>
<P>有关应用程序安全性的一般信息，请参阅 <I>Sun ONE 应用服务器开发者指南</I>。</P></TD></TR></TBODY></TABLE>
<HR SIZE=1>
<BR>
<P></P><A name=24453></A>
<P>本节介绍以下主题： </P>
<UL><A name=22005></A>
<P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#22264" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#22264">关于安全的</A><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#22264" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#22264"> Enterprise Bean</A> <A name=22016></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#15275" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#15275">定义安全角色</A> <A name=23208></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#15324" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#15324">声明方法权限</A> <A name=23213></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#15372" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#15372">声明安全角色参考</A> <A name=23319></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#23235" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#23235">指定安全</A><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#23235" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#23235">标识&nbsp;</A> <A name=23218></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#23853" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#23853">使用编程安全</A> <A name=23222></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#23184" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#23184">处理未受保护的 </A><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#23184" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#23184">EJB 层资源</A> </LI></UL><A name=24103></A>
<P>有关应用程序安全的一般信息，请参阅 <I>Sun ONE 应用服务器开发者指南</I>。</P><A name=22264></A><A name="About Secure Enterprise Beans"></A>
<H1>关于安全的 Enterprise Bean</H1>
<HR noShade SIZE=2>
<A name=22601></A>
<P>EJB 开发者的主要角色是声明您的应用程序的安全要求，应用程序开发期间，需要满足这些要求。多数情况下，EJB 的业务方法不应包含任何与安全相关的逻辑。</P><A name=23062></A>
<P>本节介绍以下主题：</P>
<UL><A name=23063></A>
<P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#22441" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#22441">授权和身份验证</A> <A name=23081></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#22646" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#22646">安全角色</A> <A name=23100></A>
<P></P>
<LI><A href="http://www.huihoo.com/middleware/application_server/sunone/app7_ejb/desecur.html#22780" tppabs="http://gceclub.sun.com.cn/staticcontent/html/sunone/app7/app7-dg-ejb/desecur.html#22780">部署</A> </LI></UL><A name=22441></A><A name="Authorization and Authentication"></A>
<H2>授权和身份验证</H2><A name=22491></A>
<P><I>授权</I>提供对受保护资源的受控访问；授权以身份识别和身份验证为基础。<I>身份识别</I>是通过一个系统实现实体认可的过程。授权是验证计算机系统中用户、设备或其他实体的身份的过程，通常作为允许访问系统中资源的前提条件。 </P><A name=22562></A>
<P>Enterprise Bean 可配置为仅允许具有适当授权级别的用户进行访问。此任务是通过利用 Sun ONE 应用服务器管理界面为应用程序 EAR 和 EJB JAR 文件生成部署描述符来完成的。</P><A name=22646></A><A name="Security Roles"></A>
<H2>安全角色</H2><A name=22736></A>
<P><I>安全角色</I>是应用程序特有的逻辑用户分组，具体按照通用特征（如客户概要或工作头衔）分类。部署一个应用程序时，可以将角色映射到安全标识，如操作环境中的<I>原则（principal）</I>（作为身份验证结果指定给用户的标识）或组。基于此，具有某种安全角色的用户对 Enterprise Bean 具有关联访问权限。链接为所引用的安全角色的实际名称。 </P><A name=24174></A>
<P>一个组也可以代表一个用户类别，但其范围不同于一个角色的范围。 </P>
<UL><A name=24179></A>
<P>
<LI>角色是 J2EE 应用程序特有的抽象概念。 <A name=24183></A>
<P></P>
<LI><I>一个组</I>是一组当前领域中环境特定的用户。组成员身份是通过基本领域实施来确定的。 <A name=24285></A>
<P><BR>
<HR SIZE=1>

<TABLE cellPadding=5 width="75%" border=0>
<TBODY>
<TR>
<TD vAlign=top><A name=24291></A><B>注意</B> </TD>
<TD vAlign=top><A name=24300></A>
<P>定义方法限制和角色映射时，将领域组和 J2EE 应用程序角色混淆起来是一个常见的错误。这样的混淆会导致不希望的访问结果或不可互操作的应用程序配置。有关领域的信息，请参阅 <I>Sun ONE 应用服务器开发者指南</I>。</P></TD></TR></TBODY></TABLE>
<HR SIZE=1>
<BR>
<P></P></LI></UL><A name=22780></A><A name=Deployment></A>
<H2>部署</H2><A name=22799></A>
<P><I>安全角色引用</I>定义使用 <SPAN class=monospace>isCallerInRole</SPAN>（字符串名称）从 <FONT face=fontsansserif color=red size=+2>E</FONT><FONT face=fontsansserif color=red size=+2>nterprise Bean</FONT> 调用的角色的名称与已为应用程序定义的安全角色的名称之间的映射。此安全性角色引用使一个 Enterprise Bean 可以引用一个现有安全角色。 </P><A name=22835></A>
<P>部署应用程序时，部署者将角色映射到操作环境中存在的全标识。开发 Enterprise Bean 时，您应知道您的用户的角色，但很可能不会准确地知道用户的真实身份。在 J2EE 安全体系架构中，对此设计非常细致。部署您的组件之后，系统管理员将角色映射到默认领域（通常为文件领域）的 J2EE 用户（或组）。</P><A name=15275></A><A name="Defining Security Roles"></A>
<H1>定义安全角色</H1>
<HR noShade SIZE=2>
<A name=22688></A>
<P>要为 J2EE 应用程序创建一个角色，需要为 EJB JAR 文件或应用程序中包含的 WAR 文件声明该角色。<SPAN class=monospace>security-role</SPAN> 元素中定义的安全角色的范围限制在 EJB JAR 文件级别，并适用于 EJB JAR 文件中的所有 Enterprise Bean。</P><A name=24317></A><A name=Example></A>
<H4>示例</H4><A name=15298></A>
<P>部署描述符中一个安全角色定义的以下示例指定了两个角色名称元素 <SPAN class=monospace>employee</SPAN> 和 <SPAN class=monospace>admin</SPAN>。</P><A name=24067></A>
<P><SPAN class=codeline>... <BR>&lt;assembly-descriptor&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;security-role&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This role includes the employees of the enterprise who<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;are allowed to access the employee self service<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;application. This role is allowed to access only<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;her/his information <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/desciption&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;role-name&gt;<B>employee</B>&lt;role-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/security-role&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;security-role&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This role should be assigned to the personnel<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authorized to perform administrative functions<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for the employee self service application. This<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;role does not have direct access to<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sensitive employee and payroll information<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/desciption&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;role-name&gt;<B>admin</B>&lt;role-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/security-role&gt; <BR>... <BR>&lt;/assembly-descriptor&gt; </SPAN></P><A name=15324></A><A name="Declaring Method Permissions "></A>
<H1>声明方法权限</H1>
<HR noShade SIZE=2>
<A name=22862></A>
<P><I>方法权限</I>表示允许哪些角色调用哪些方法。应用程序汇编者使用如下所示方法权限元素在部署描述符中声明方法权限： </P>
<UL><A name=15329></A>
<P>
<LI>每个 <SPAN class=monospace>method-permission</SPAN> 元素都有一个包含一个或多个安全角色的列表以及一个包含一个或多个方法的列表。 
<DL><A name=22156></A>
<DT>
<P>列出的所有安全角色都可以调用列出的所有方法。列表中的每个安全角色通过 <SPAN class=monospace>role-name</SPAN> 元素识别，而每个方法（或方法集，如下面所述）则通过方法元素识别。可以使用说明元素将一个可选说明与 <SPAN class=monospace>method-permission</SPAN> 元素关联。 </P></DT></DL><A name=15335></A>
<P></P>
<LI>方法权限关系作为个别方法权限元素中定义的所有方法权限的集合进行定义。 <A name=15338></A>
<P></P>
<LI>一个安全角色或一个方法可以出现在多个 <SPAN class=monospace>method-permission</SPAN> 元素中。 </LI></UL><A name=24321></A><A name=Example></A>
<H4>示例</H4><A name=24328></A>
<P>以下部署描述符示例说明如何在部署描述符中给安全角色指定方法权限。部署时将这些方法权限转换为安全元素。 </P><A name=15343></A>
<P><SPAN class=codeline>... <BR>&lt;method-permission&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;role-name&gt;employee&lt;/role-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;method&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-name&gt;EmployeeService&lt;/ejb-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;method-name&gt;*&lt;/method-name&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;/method&gt; <BR>&lt;/method-permission&gt; <BR></SPAN></P><A name=15352></A>
<P><SPAN class=codeline>&lt;method-permission&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;role-name&gt;employee&lt;/role-name&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;method&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-name&gt;AardvarkPayroll&lt;/ejb-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;method-name&gt;findByPrimaryKey&lt;/method-name&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;/method&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;method&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-name&gt;AardvarkPayroll&lt;/ejb-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;method-name&gt;getEmployeeInfo&lt;/method-name&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;/method&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;method&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-name&gt;AardvarkPayroll&lt;/ejb-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;method-name&gt;updateEmployeeInfo&lt;/method-name&gt; <BR>&nbsp;&nbsp;&nbsp;&lt;/method<BR>&lt;/method-permission&gt; <BR>... </SPAN></P><A name=15372></A><A name="Declaring Security Role References "></A>
<H1>声明安全角色引用</H1>
<HR noShade SIZE=2>
<A name=15374></A>
<P>EJB 开发者应负责声明所有安全角色名称的，它们用于角色的部署描述符中的 <SPAN class=monospace>security-role-ref</SPAN> 元素，这些角色由程序从各自 Enterprise Bean 中进行使用。 </P>
<UL><A name=15377></A>
<P>
<LI>应用程序汇编者负责将在 <SPAN class=monospace>security-role-ref</SPAN> 元素中声明的所有安全角色引用链接到 <SPAN class=monospace>security-role</SPAN> 元素中定义的安全角色。 <A name=22180></A>
<P></P>
<LI>应用程序汇编者使用 <SPAN class=monospace>role-link</SPAN> 元素将每个安全角色引用链接到一个安全角色。 <BR>
<HR SIZE=1>

<TABLE cellPadding=5 width="75%" border=0>
<TBODY>
<TR>
<TD vAlign=top><A name=22188></A><B>注意</B> </TD>
<TD vAlign=top><A name=22203></A>
<P><SPAN class=monospace>role-link</SPAN> 元素值必须是 <SPAN class=monospace>security-role</SPAN> 元素中定义的安全角色名称之一。 </P></TD></TR></TBODY></TABLE>
<HR SIZE=1>
<BR></LI></UL><A name=24334></A><A name=Example></A>
<H4>示例</H4><A name=15384></A>
<P>以下部署描述符示例说明如何将名为 <SPAN class=monospace>payroll</SPAN> 的安全角色引用链接到名为 <SPAN class=monospace>payroll-department </SPAN>的安全角色。 </P><A name=24043></A>
<P><SPAN class=codeline>&lt;enterprise-beans&gt; <BR>&nbsp;&nbsp;&nbsp;... &nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&lt;entity&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-name&gt;AardvarkPayroll&lt;/ejb-name&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ejb-class&gt;com.aardvark.payroll.PayrollBean&lt;/ejb-class&gt; <BR>&nbsp;&nbsp;&nbsp;... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;security-role-ref&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt; This role should be assigned to the payroll<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;department's employees. Members of this role have access to<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anyone's payroll record. The role has been linked to the<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;payroll-department role.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/description&gt; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;role-name&gt;payroll&lt;/role-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;role-link&gt;payroll-department&lt;/role-link&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/security-role-ref&gt; <BR>&nbsp;&nbsp;&nbsp;.... <BR>&nbsp;&nbsp;&nbsp;&lt;/entity&gt; <BR>&nbsp;&nbsp;&nbsp;... <BR>&lt;/enterprise-beans&gt; </SPAN></P><A name=16993></A>
<P>应把此角色指定给薪资部的员工。此角色的成员可以访问任何人的薪资记录。该角色已被链接到 <SPAN class=monospace>payroll-department</SPAN> 角色。 </P><A name=24219></A>
<P>有关安全角色的进一步信息，请参阅 <I>Sun ONE 应用服务器开发者指南</I>。有关 EJB 访问控制配置的详细信息，请参阅 Enterprise JavaBeans Specification 2.0。</P><A name=23235></A><A name="Specifying Security Identities"></A>
<H1>指定安全标识</H1>
<HR noShade SIZE=2>
<A name=24231></A>
<P>EJB 汇编者可以随意指定是否将调用者的标识用于执行 EJB 方法，或指定是否使用 run-as 身份。出于此目的，使用部署描述符中的 <SPAN class=monospace>security-identity</SPAN> 元素。<SPAN class=monospace>security-identity</SPAN> 元素的值为 <SPAN class=monospace>use-caller-identity</SPAN> or <SPAN class=monospace>run-as</SPAN>。 </P><A name=24235></A>
<P>如果没有指定，默认情况下使用调用者标识。. </P><A name=23255></A><A name="The run-as Identity"></A>
<H2>run-as 标识</H2><A name=23259></A>
<P><I>run-as 标识</I>建立 Enterprise Bean 进行调用操作时将使用的标识。 run-as 标识不会影响其调用这的标识，这些标识是针对访问 Enterprise Bean 的方法权限而测试的标识。</P><A name=23284></A>
<P>EJB 汇编者可以使用 <SPAN class=monospace>run-as</SPAN> 元素为部署描述符中的 Enterprise Bean 定义一个 run-as 标识。run-as 标识总体上适用于 Enterprise Bean，即适用于 EJB 的主接口和组件接口的所有方法 EJB，或适用于消息驱动型 Bean 的 <SPAN class=monospace>onMessage</SPAN> 方法以及可能调用的 Enterprise Bean 的所有内部方法。</P><A name=23286></A>
<P>由于汇编者一般不知道操作环境的安全环境，因而按一个<I>逻辑</I>角色名指定 run-as 标识，该逻辑角色名与部署描述符中定义的安全角色之一相对应。然后，部署者必须指定一个作为 run-as 标识的原则使用的安全原则（在操作环境中定义）。安全原则应是一个已经指定给安全角色的与 <SPAN class=monospace>role-name</SPAN><SPAN class=monospace> </SPAN>元素指定的相同的原则。</P><A name=23853></A><A name="Using Programmatic Security"></A>
<H1>使用编程安全</H1>
<HR noShade SIZE=2>
<A name=23855></A>
<P>一般来说，应由容器以一种对 EJB 的业务方法透明的方式执行安全管理功能。 </P><A name=23163></A>
<P><BR>
<HR SIZE=1>

<TABLE cellPadding=5 width="75%" border=0>
<TBODY>
<TR>
<TD vAlign=top><A name=23166></A><B>注意</B> </TD>
<TD vAlign=top><A name=23172></A>
<P>就像服务件的做法一样，Enterprise Bean 可以使用编程登录。有关详细信息，请参阅 Sun ONE 应用服务器<I>开发者指南</I></P></TD></TR></TBODY></TABLE>
<HR SIZE=1>
<BR>
<P></P><A name=23006></A>
<P>EJB 层中的编程安全由 <SPAN class=monospace>getCallerPrincipal</SPAN> 和 <SPAN class=monospace>isCallerInRole</SPAN> 方法组成。您可以使用 <SPAN class=monospace>getCallerPrincipal</SPAN> 方法确定 Enterprise Bean 的调用者，以及使用 <SPAN class=monospace>isCallerInRole</SPAN> 方法确定调用者的角色。 </P><A name=22889></A>
<P><SPAN class=monospace>EJBContext</SPAN> 接口的 <SPAN class=monospace>getCallerPrincipal</SPAN> 方法返回识别 Enterprise Bean 的调用者的 <SPAN class=monospace>java.security.Principal</SPAN> 对象。（在此情况下，<FONT color=#000000>原则</FONT>与一个用户相同。在以下示例中，一个 Enterprise Bean 的 <SPAN class=monospace>getUser</SPAN> 方法返回调用它的 J2EE 用户的名称： </P><A name=22893></A>
<P><SPAN class=codeline>&nbsp;&nbsp;&nbsp;public String getUser() <BR>&nbsp;&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return context.getCallerPrincipal().getName(); <BR>&nbsp;&nbsp;&nbsp;} </SPAN></P><A name=22898></A>
<P>　</P><A name=22214></A>
<P><SPAN class=codeline>&nbsp;&nbsp;&nbsp;boolean result = context.isCallerInRole("Customer"); </SPAN></P><A name=23189></A>
<P>有关如何实施编程安全的详细信息，请参阅 Enterprise JavaBeans Specification 2.0 的第 21 章“安全管理”。</P><A name=23184></A><A name="Handling Unprotected EJB-Tier Resources "></A>
<H1>处理不受保护的 EJB 层资源</H1>
<HR noShade SIZE=2>
<A name=23610></A>
<P>所有用户都具有匿名角色。默认情况下，匿名角色的值为 ANYONE，该值可在 <SPAN class=monospace>server.xml</SPAN> 文件中进行配置。因此，如果某个方法权限指定所需角色为 ANYONE（或将匿名角色设置为的任何值），则任何用户都可以访问此方法。 </P><A name=24251></A>
<P><BR>
<HR SIZE=1>

<TABLE cellPadding=5 width="75%" border=0>
<TBODY>
<TR>
<TD vAlign=top><A name=24254></A><B>注意</B> </TD>
<TD vAlign=top><A name=24263></A>
<P>如果覆盖某个方法的方法权限不存在，则任何人都可以访问该方法。 </P></TD></TR></TBODY></TABLE>
<HR SIZE=1>
<BR>
<P></P><A name=24051></A>
<P>如果存在方法权限，就始终执行此方法权限。例如，如果设置了某个方法权限，因而 <SPAN class=monospace>updateEmployeeInfo</SPAN> 方法只能由 <SPAN class=monospace>employee</SPAN> 角色进行访问，那么，如果没有角色 <SPAN class=monospace>employee</SPAN>，就始终无法访问此方法。如果没有把 <SPAN class=monospace>employee</SPAN> 角色映射到任何用户或组，就没有人能够调用 <SPAN class=monospace>updateEmployeeInfo</SPAN> 方法。</P></BOOKCONTENT><FOOTERCONTENT>
<BLOCKQUOTE><BR></BLOCKQUOTE><img src ="http://www.blogjava.net/kapok/aggbug/4335.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-16 14:11 <a href="http://www.blogjava.net/kapok/archive/2005/05/16/4335.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用BEA WSRP测试服务器 </title><link>http://www.blogjava.net/kapok/archive/2005/05/08/4087.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 07:33:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/08/4087.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4087.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/08/4087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4087.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4087.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/webplat/2005010903.html">http://dev2dev.bea.com.cn/techdoc/webplat/2005010903.html</A><BR><BR>用于远程Portlet的Web服务（Web Services for Remote Portlets，WSRP）是OASIS创建的一种面向表示的web服务标准，它允许在具有门户的可视、用户互动web服务或者其他中间web应用程序中实现即插即用。WSRP允许网络中的远程门户资源可以在本地门户中提供。BEA在2004年2月发布了一款WSRP技术预览套件，在2004年7月为这款产品添加了官方支持。您必须使用具有WSRP的WebLogic Portal 8.1 SP3或者更新的版本。<BR>　　如果您对WSRP不熟悉，访问本文末尾中的有用链接会有很大的帮助。这将确保您熟悉WSRP的主要概念以及它们在WebLogic Portal 8.1 SP3中的工作方式。<BR><BR><B>可提供什么?</B><BR>　　BEA已经在公共测试服务器上提供了一个远程portlet示例，来帮助开发人员深入掌握WSRP。
<P align=center><IMG height=482 src="http://dev2dev.bea.com.cn/techdoc/webplat/image2005010914.gif" width=600></P>
<P>　　目前，我们已经拥有了5个可以通过WSRP访问的驻留portlet。这些portlet是：<BR>　　BEA: WSRP<BR>　　BEA: Racing!<BR>　　BEA: Double Byte<BR>　　BEA: SOA - eWorld 2004<BR>　　BEA: Download WebLogic Portal 8.1<BR><BR>您需要的主要URL是:</P>
<OL>
<LI>WebLogic Portal 8.1 SP3测试服务器：<A href="http://wsrp.bea.com/" target=_blank>http://wsrp.bea.com</A> 
<LI>具有WSRP portlet的WSRP门户示例：<A href="http://wsrp.bea.com/portal/wsrp.portal%20" target=_blank>http://wsrp.bea.com/portal/wsrp.portal</A> 
<LI>WSDL Address：<A href="http://wsrp.bea.com/portal/producer?wsdl" target=_blank>http://wsrp.bea.com/portal/producer?wsdl</A> </LI></OL>
<P>　　为了在WebLogic Portal项目中支持远程portlet，您可以使用Workshop或者Portal Administration工具。Workshop使您可以通过portlet向导来创建一个引用远程portlet的代理portlet。Administration Portal使您可以通过添加Producer来创建代理portlet。<BR><BR><B>从Workshop使用WSRP测试服务器</B><BR>　　在这个例子中，我们在一个门户域中使用具有Portal Application的Workshop（您可以使用其他的配置 — 参见“ Working with Producers ”）。为了创建代理portlet：</P>
<OL>
<LI>右键点击Portal project，选择“New”→“Portlet”。 
<LI>在Portlet Wizard中选择“Remote Portlet”选项。 
<LI>将以下测试服务器WSDL粘贴到Remote Server字段中： <A href="http://wsrp.bea.com/portal/producer?wsdl" target=_blank>http://wsrp.bea.com/portal/producer?wsdl</A>。 
<LI>根据Wizard提示来注册Producer。 
<LI>选择一个portlet和应用程序联合，例如：“BEA: eWorld 2004”。 
<LI>完成以后，您将得到一个.portlet 文件。.portlet应该文件存放在什么地方呢？ 
<LI>把 portlet 添加到门户中，用拖放操作把它放置在所需的位置上（book、page等）。 
<LI>运行门户来察看远程portlet。 </LI></OL>
<P align=center><IMG height=350 src="http://dev2dev.bea.com.cn/techdoc/webplat/image2005010915.gif" width=600></P>
<P><B>从Administration Portal使用WSRP测试服务器</B><BR>　　在访问Administration Portal之前，您必须首先运行WebLogic Portal服务器。您应当运行WebLogic Portal 8.1 SP3或者更新的版本。<BR>　　使用Administration Portal，按照以下的说明来创建代理portlet：</P>
<OL>
<LI>在Portal选项卡中，展开左边的Portal Library。 
<LI>右键点击“Portlet Producer”文件夹，选择“Add Producers”。 
<LI>在Add Producer对话框中，将测试服务器WSDL粘贴到Producer字段中：<A href="http://wsrp.bea.com/portal/producer?wsdl">http://wsrp.bea.com/portal/producer?wsdl</A> 
<LI>点击“Add Producer”创建到producer的引用。 
<LI>键入Producer Property。 
<LI>点击“Register”确立与producer的关系。 
<LI>注册完成后，点击producer链接察看所提供的远程portlet（参见下图）。 
<LI>点击“Add”激活portlet库中的远程portlet。 
<LI>将portlet添加到桌面。 
<LI>如果想预览远程portlet，右键点击桌面，并选择“View Desktop”。 </LI></OL>
<P align=center><IMG height=541 src="http://dev2dev.bea.com.cn/techdoc/webplat/image2005010916.gif" width=600></P>
<P>　　您还可以使用WebLogic Portal 8.1 SP3或更新版本将自己的portlet（Page Flow、Struts、JSR168）作为远程portlet发布。如果使用WebLogic Server 8.1 SP3或者更新版本，您可以把使用Page Flow和Struts创建的应用程序作为WSRP资源发布。要获取更多关于使用WSRP的信息，请访问以下链接：</P>
<OL>
<LI>利用WebLogic Portal 8.1使用Web Services for Remote Portlets (WSRP):<BR><A href="http://dev2dev.bea.com/products/wlportal81/articles/wsrp.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/articles/wsrp.jsp</A> 
<LI>WSRP Standards 站点：<BR><A href="http://www.oasis-open.org/committees/wsrp/" target=_blank>http://www.oasis-open.org/committees/wsrp/</A> 
<LI>BEA WebLogic Portal 8.1 相关站点：<BR><A href="http://dev2dev.bea.com/products/wlportal81/index.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/index.jsp</A><BR><A href="http://e-docs.bea.com/wlp/docs81/wsrp/index.html" target=_blank>http://e-docs.bea.com/wlp/docs81/wsrp/index.html</A><BR><A href="http://edocs.bea.com/wlp/docs81/index.html" target=_blank>http://edocs.bea.com/wlp/docs81/index.html</A> 
<LI>网络培训：WSRP和 Federated Portals<BR><A href="http://dev2dev.bea.com/trainingevents/webinars/060904_wsrp.jsp" target=_blank>http://dev2dev.bea.com/trainingevents/webinars/060904_wsrp.jsp</A> </LI></OL>
<P>原文出处<BR><A href="http://dev2dev.bea.com/products/wlportal81/articles/wsrp_test_server_at.jsp" target=_blank>http://dev2dev.bea.com/products/wlportal81/articles/wsrp_test_server_at.jsp</A> </P><img src ="http://www.blogjava.net/kapok/aggbug/4087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-08 15:33 <a href="http://www.blogjava.net/kapok/archive/2005/05/08/4087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Portal中文问题解决 </title><link>http://www.blogjava.net/kapok/archive/2005/05/08/4081.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 05:55:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/08/4081.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4081.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/08/4081.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4081.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4081.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD bgColor=#d9d9d9 colSpan=11>
<TABLE cellSpacing=0 cellPadding=3 width="100%" border=0>
<TBODY>
<TR>
<TD class=darkgreylarge><A href="http://dev2dev.bea.com.cn/bbs/school/guide/wlportal/2004122101.html">http://dev2dev.bea.com.cn/bbs/school/guide/wlportal/2004122101.html</A><BR><BR><BR>标题：Portal中文问题解决 </TD>
<TD>浏览次数：
<SCRIPT language=JavaScript src="http://203.81.25.199/cgi-bin/beadevcount.cgi?d_id=92&amp;type=school"></SCRIPT>
 1362 </TD>
<TD align=right>时间：2004-12-21</TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD bgColor=#d9d9d9 colSpan=11><IMG height=5 alt="" src="http://dev2dev.bea.com.cn/images/spacer.gif" width=4 border=0></TD></TR>
<TR>
<TD bgColor=#d9d9d9 colSpan=11>
<TABLE cellSpacing=0 cellPadding=3 width="100%" border=0>
<TBODY>
<TR>
<TD>作者：刘群策（dev2dev ID:hbwind） BEA系统（中国）有限公司 大企业客户部资深技术顾问</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>　本文从台湾BEA网站相关文章改编而来，加入了笔者的一些注释和工具。<BR><BR><B>1.1 如何构建以 GBK 编码为基础的 Applications 项目</B><BR>　　在 Workshop 中，所有源文件 (JSP、PageFlow、.portal…) 的预设编码皆为 UTF-8，如果您想要用 GBK 来作为 PageFlow 或 JSP 的编码 (例如有以前写的GBK或者GB2312 编码的 JSP 想整合进来)，请按照下列步骤做调整。<BR>如要建立以 GBK 为预设编码的 JSP 或 PageFlow，需作以下调整：
<OL>
<LI>修改 JSP 第一行的 ContentType 宣告，将 UTF-8 改为 GBK/GB2312，即： <BR>&lt;%@ page language="java" contentType="text/html;charset=GBK"%&gt; 
<LI>编辑 weblogic.xml中，关于 encoding 这项的设定，将 UTF-8 改为 GBK，即：<BR>&lt;jsp-param&gt;<BR>&lt;param-name&gt;encoding&lt;/param-name&gt;<BR>&lt;param-value&gt;GBK&lt;/param-value&gt;<BR>&lt;/jsp-param&gt; 
<LI>变更 Web 应用程序字符编码<BR>在 Workshop IDE 中，从 Tools 选单 -&gt; Application Properties -&gt; Encoding 选项中，将 Web App Character Encoding 底下的 "Use default (UTF-8)" 一项，移除勾选，并在 "Encoding" 字段后键入 GBK或gb2312。 
<LI>将 charset-params 项目加到 weblogic.xml 中：<BR>&lt;charset-params&gt;<BR>&lt;input-charset&gt;<BR>&lt;resource-path&gt;/*&lt;/resource-path&gt;<BR>&lt;java-charset-name&gt;GBK&lt;/java-charset-name&gt;<BR>&lt;/input-charset&gt;<BR>&lt;/charset-params&gt; 
<LI>将以下项目从 web.xml 中移除或批注掉，或者将param-value修改为GBK<BR>&lt;context-param&gt;<BR>&lt;param-name&gt;weblogic.httpd.inputCharset./*&lt;/param-name&gt;<BR>&lt;param-value&gt;UTF-8&lt;/param-value&gt;<BR>&lt;/context-param&gt; 
<LI>将 WLP8.1 portal 模板(即 .portal 文件) 的 ContentType 编码加以修改 (预设编码为 UTF-8) 如下：<BR>&lt;portal:directive.page contentType="text/html;charset=GBK"/&gt; <BR><BR><B>变更方法</B><BR>(由于 .portal 档的本质为 XML，建议使用 Workshop 来编辑该档案，比较安全)<BR>在 Workshop IDE 中，从 Tools 选单中选择 "Application Properties" <BR>选择 "Portal" 的选项，然后勾选 "Open as XML Option" 选项 <BR>接着便可以以右键点选您的 .portal 档案，并选择 "Open as XML" 指令 <BR>按照以上的说明，编辑 .portal 文件的 XML 原始码，修改编码部分的宣告 <BR>关闭当前文件，再打开.portal文件时，又是图形化界面<BR><BR><B>执行到这一步时，已经能够保证运行.portal文件的编码是简体中文了。<BR></B>
<LI>使用 WebLogic Administration Portal，以 GBK 型建立之 Portal 为基础来建立桌面时，必须更新数据库表数据。 <BR>更新对象表：L10N_LOCALE<BR>更新对象列：ENCODING (标准设置是 UTF-8) </LI></OL>
<P><B>变更方法和执行步骤如下。</B><BR>　　部署 Portal 应用程序。 <BR>　　启动 WebLogic Administration Portal 并登入。 <BR>　　在数据库的 L10N_LOCALE 表中，寻找LANGUAGE =’zh’ 的 记录，然后将ENCODING 列从 UTF-8 修改为 GBK。 <BR>　　使用 WebLogic Administration Portal 来建立新的桌面。以后建立的所有桌面都是 GBK 型桌面。</P>
<P><B>补充 1. 表更新时机</B><BR>　　在 L10N_LOCALE 表中，必须变更下述两列的 ENCODING 列：分别是 LANGUAGE 值为 zh 且 COUNTRY 值为 CN 的行，以及 LANGUAGE 值为 zh 的行。LANGUAGE 值为 zh 且 COUNTRY 值为 CN 的行，会在部署 Portal 应用程序时自动建立，而 LANGUAGE 值为 zh 的行，则会因为 Portal 应用程序所建立的内容，而未在部署时登录。 在这种情况下，此列会在登入 WebLogic Administration Portal 时自动建立。因此，在登入 WebLogic Administration Portal 之后再变更表较为适当。</P>
<P><B>范例：更新 Oracle9i 的列</B><BR>　　SQL&gt; UPDATE L10N_LOCALE SET ENCODING='GBK' WHERE LANGUAGE='zh'; 两列已更新。 <BR>　　SQL&gt; commit;</P>
<P><B>范例：更新 PointBase 的列</B><BR>　　由于缺省时，Portal带有的数据库是Pointbase，所以，进入Domain目录，运行startPointBaseConsole.cmd或者startPointBaseConsole.sh，然后确定Concole里面URL是jdbc:pointbase:server://10.130.2.150:9093/workshop，输入用户名称和密码都是weblogic/weblogic，在Enter SQL Commands输入框中输入<BR>　　UPDATE L10N_LOCALE SET ENCODING='GBK' WHERE LANGUAGE='zh'; <BR>　　点击Toolbar上的Execute All ，执行完毕。</P>
<P>　　<B>执行完该步骤时，才能保证streaming portal运行支持简体中文。</B></P>
<P><B>1.2 在 Portal 应用程序中添加版面、外壳以及观感的简体中文登录信息的方法</B><BR>　　如果在 Portal 管理画面中看到以下的讯息：<BR>　　尚未设置 [zh_CN] 语言地区的标题。</P>
<P>　　这是因为版面、外壳以及观感等 Portal 样板的简体中文信息尚未登录所致。登录信息的默认值为 "en"，如果要显示其它语言地区，必须执行下列步骤。</P>
<P>　　通过Portal 管理工具的修正方式：<BR>　　启动 portalAdmin (将浏览器的语言设定为 'en') <BR>　　选择 PortalResources-Layout <BR>　　为树状目录中显示的每个项目选择 '添加语言地区'。(设定想要添加的语言 (例如，'zh_CN')，然后说明标题。) <BR>　　回复浏览器初始的语言设定，然后重新显示。 <BR>　　透过这些步骤，可以将版面、外壳以及观感的登录信息储存在数据库中 (表名称：L10N_RESOURCE)。</P>
<P>　　通过Workshop 的修正方式：<BR>　　在 Workshop 中建立的 Portal Web project 的 framework\markup\ 数据夹中，有 fourcolumn.layout、text.laf 等样板档案。如果事先将这些档案中的语言地区值设定为 "zh"，则上述讯息就不会显示出来。所有样版的原始档都来自 %WL_HOME%\workshop\templates\portal-project.zip 中的 portal.war。</P>
<P align=center><A href="http://dev2dev.bea.com.cn/bbs/school/guide/wlportal/project.zip">下载portal-project.zip</A></P>
<P>　　通过命令方式：<BR>　　解开压缩包，拷贝com目录和changelocale.cmd到domain目录下<BR>　　打开changelocale.cmd，查看URL，username和password，确保与当前Domain相符。<BR>　　运行setdomainenv.cmd<BR>　　运行changelocale.cmd<BR>　　由于直接操作数据库，所以该命令适合Weblogic Portal8.13（不能保证其他版本数据库表结构不会发生变化），主要是为各个资源建立zh_CN相关的数据库表记录，注意的是，这样修改不会马上在管理工具中生效，需要等待一段时间。</P><img src ="http://www.blogjava.net/kapok/aggbug/4081.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-08 13:55 <a href="http://www.blogjava.net/kapok/archive/2005/05/08/4081.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LDAP 问题</title><link>http://www.blogjava.net/kapok/archive/2005/05/08/4080.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 05:35:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/08/4080.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4080.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/08/4080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4080.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4080.html</trackback:ping><description><![CDATA[<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><U style="COLOR: rgb(0,0,0)"><B><A name=Problem_Description></A><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html">http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html</A><BR><BR>问题描述</B></U><BR>
<UL>
<LI>Weblogic Server 无法连接到 LDAP 或无法找到用户/组来执行角色映射。 
<LI>内嵌的 LDAP 锁定问题。 
<LI>与客户端身份验证相关的性能问题。 </LI></UL>下面是六个常见的故障症状： 
<OL>
<LI>Authentication Provider 无法连接到 LDAP。 
<LI>用户无法验证身份。 
<LI>用户可以验证身份，但 Authentication Provider 无法找到此用户所属的组。 
<LI>用户可以验证身份，但没有所需的角色。 
<LI>服务器因内嵌的 LDAP 锁定问题而无法启动。 
<LI>对 LDAP 的客户端身份验证速度太慢。 </LI></OL></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><SPAN style="FONT-WEIGHT: bold; TEXT-DECORATION: underline"><A name=Problem_Troubleshooting></A>故障排除</SPAN><BR>请注意，并非下面所有任务都需要完成。有些问题仅通过执行几项任务就可以解决。<BR><BR><SPAN style="FONT-WEIGHT: bold">快速链接</SPAN><BR>
<UL>
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#What_is_LDAP_and_how_it_is_used_by_WLS">什么是 LDAP，WLS 如何使用它？</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Common_LDAP_servers_used_in_WLS">WLS 中常用的 LDAP 服务器。</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Differences_between_WLS_6.x_7.x_and_8.x">WLS 6.x、WLS 7.x 和 WLS 8.x 之间的差异</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Troubleshoot_connection_problems">排除连接故障</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Troubleshoot_user_authentication_failure">排除用户身份验证故障</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Troubleshoot_group_membership_problems">排除组成员资格故障</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Troubleshoot_role_mapping_failure">排除角色映射故障</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#How_to_get_Configuration_data">如何获得配置数据</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#What_is_the_Control_Flag_in_Authentication_Provider">什么是 Authentication Provider 中的控制标志？</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Enumeration_of_usersgroups_in_console">在控制台中列举用户/组</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Performance_Issues">性能问题</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Embedded_LDAP_issues">内嵌的 LDAP 问题</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Debug_Flags">调试标志&nbsp;</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Tools">工具</A> 
<LI><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Examples_of_Authentication_Providers">Authentication Provider 的示例</A> </LI></UL></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><A name=What_is_LDAP_and_how_it_is_used_by_WLS></A><FONT style="COLOR: rgb(0,0,0); TEXT-DECORATION: underline" color=#009900><B>什么是 LDAP，WLS 如何使用它？</B></FONT><BR>轻量型目录访问协议 (LDAP) 是程序用于从服务器中查找联系信息的一个协议。与关系数据库很相似，每个 ldap 数据库（或目录）都拥有一个 schema。LDAP schema 是作为一组对象类定义和属性定义来实现的。<BR><BR>LDAP 中使用的一些常用术语是：<BR>
<UL>
<LI>dc= 域组件 
<LI>o = 组织 
<LI>ou= 组织单位 
<LI>cn= 公用名 
<LI>dn= 辨别名 
<LI>uid= 用户 ID </LI></UL>当您需要在 LDAP 中搜索条目时，必须提供搜索的基准和过滤器。<BR>例如：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="FONT-FAMILY: courier new">ldapsearch .b "dc=beasys,dc=com" uid=fred</FONT><BR></DIV><BR>这将返回符合过滤器中所指定条件 uid=fred 的用户的所有属性。<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">uid=fred,ou=People, dc=beasys,dc=com&nbsp;&nbsp; </SPAN><B style="FONT-FAMILY: courier new">// 这就是所谓的“完整 DN”(Full DN)</B><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">objectClass=top</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">objectClass=person</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">objectClass=organizationalPerson</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">objectClass=inetorgperson</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">givenName=Fred</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">cn=Fred A</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">uid=fred</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">sn=A</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">creatorsName=uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">modifiersName=uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">createTimestamp=20040108160418Z</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">modifyTimestamp=20040108162837Z</SPAN><BR></DIV><BR>此条目在 LDAP 树中的形式如下：&nbsp;&nbsp;<BR><BR>
<DIV style="MARGIN-LEFT: 40px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dc=beasys,dc=com&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;(root)&nbsp;&nbsp;&nbsp;&nbsp;\<BR>&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; /&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; \<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; /&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;\<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ou=people&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ou=otherusers<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uid=fred<BR></DIV><BR>WLS 可以从外部目录服务器读取和搜索信息，这样就可以获得正确的配置信息，进行连接、绑定、搜索（基准和过滤器）等。<BR><BR>为此，您需要在 WLS 中配置下列字段：<BR>
<UL>
<LI>LDAP 服务器主机和端口 
<LI>用于连接和进行搜索的用户/密码 
<LI>用户基准 DN：在进行用户搜索时用作基准 
<LI>用户过滤器：在进行用户搜索时用作过滤器 
<LI>组基准 DN：在进行组搜索时用作基准 
<LI>组过滤器：在进行组搜索时用作过滤器 
<LI>组成员资格过滤器：在执行搜索以查找用户所属的组时用作过滤器 </LI></UL>在这些字段内，您可以找到下列字符：<BR><BR>
<DIV style="MARGIN-LEFT: 40px">&nbsp;%u 将用用户 ID 替代<BR>&nbsp;%g 将用组替代<BR>&nbsp;%M 将用用户的完整 DN 替代<BR></DIV><BR>当 WLS 需要验证某个用户存在并拥有访问资源的权限/角色时，它使用所定义的 Principal 用户连接到 LDAP，然后对该用户进行搜索，确认其是否存在，并用其密码进行验证。然后，它搜索该用户所属的组。<BR><BR>下面是用于成员资格检查算法的一些简洁伪代码：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">check_membership(group g, principal p):</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">&nbsp; for each group g1 that contains p directly:</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp; if g1 == g or check_membership(g, g1)</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp; then return success</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">&nbsp; return failure</SPAN><BR></DIV><BR>一旦完成了上述步骤，WLS 将根据是允许访问还是拒绝访问，确认属于已找到的组的用户是否拥有访问所请求资源的权限/角色。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><FONT color=#009900><SPAN style="FONT-WEIGHT: bold; COLOR: rgb(0,0,0)"><A name=Common_LDAP_servers_used_in_WLS></A><SPAN style="TEXT-DECORATION: underline">WLS 中常用的 LDAP 服务器</SPAN></SPAN></FONT><BR>WLS 中常用的 LDAP 服务器（作为 WLS 6.x 中的 LDAP 自定义 Realm 或较高 WLS 版本中的 Authentication Provider）是：<BR>
<UL>
<LI>缺省的 Authentication Provider（内嵌 LDAP） － 仅限于 WLS 7.x 和 WLS 8.x<BR>
<LI>Iplanet 
<LI>Active Directory 
<LI>OpenLDAP 
<LI>Novell </LI></UL><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Differences_between_WLS_6.x_7.x_and_8.x></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>WLS 6.x、WLS 7.x 和 WLS 8.x 之间的差异</B></U></FONT><BR><B>WLS 6.x</B><BR>LDAP Realm 被定义为自定义 Realm，其 Realm 类名被设置为 <FONT style="FONT-FAMILY: courier new">weblogic.security.ldaprealmv2.LDAPRealm</FONT>。配置数据包含连接、搜索等操作所需的所有数据，并且必须已定义了下列设置：<BR>
<UL>
<LI><SPAN style="FONT-FAMILY: courier new">server.host</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">server.port</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">server.principal</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">user.filter</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">user.dn</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">group.filter</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">group.dn</SPAN> 
<LI><SPAN style="FONT-FAMILY: courier new">membership.filter</SPAN> </LI></UL>然后，自定义 Realm 被映射到一个缓存 Realm 上，该缓存 Realm 反过来也被选择为服务器的缺省 Realm。<BR><BR>注意，在同一时间只有一个 Realm 是活动的。<BR>&nbsp;&nbsp;&nbsp; <BR>WLS 尝试在启动时连接到 LDAP 服务器。如果您将 WebLogic Server 配置为使用自定义安全 Realm，而该 Realm 是不可用的，则 WebLogic Server 将不启动。在 6.1SP6 中引入了一个新的启动命令 <FONT style="FONT-FAMILY: courier new">-Dweblogic.security.RealmFailureOk=true</FONT>，当该 Realm 不可用时，此命令将使服务器启动。此命令强制服务器使用 File Realm 来启动，而不是使用所配置的自定义 Realm。<BR><BR><B>WLS 7.x 和 WLS 8.x</B><BR>WLS 7.x 和 WLS 8.x 拥有一个新的安全体系结构。&nbsp;有关详细信息，请参阅：&nbsp;<A href="http://edocs/wls/docs70/secintro/realm_chap.html#1033368">http://edocs/wls/docs70/secintro/realm_chap.html#1033368</A><BR><BR>如果您正在从 WLS 6.x 升级，那么 6.x 中定义的安全 Realm 将在兼容性范围内以相同的方式定义。<BR><BR>WLS 在启动时不会连接到 LDAP 服务器，除非它需要验证用于启动的用户身份。当 WLS 需要验证某个用户身份时，它打开一个 ldap 连接，这一点与 6.x 不同，在 6.x 中总是在启动时建立一个连接。<BR><BR>注意，可以有多个 Authentication Provider 处于活动状态。缺省的 LDAPRealm 是内嵌的 LDAP Realm。<BR><BR>在 WLS 7.x / 8.x 安全模式下要定义 Authentication Provider，LDAP 服务器是 Authentication Provider。下面的文件里定义了常见的 Authentication Provider：&nbsp;<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="FONT-FAMILY: courier new">%WL_HOME%\server\lib\mbeantypes\wlSecurityProviders.jar</FONT><BR></DIV><BR>您可以开发一个自定义Authentication Provider，并复制到 <SPAN style="FONT-FAMILY: courier new">%WL_HOME%\server\lib\mbeantypes</SPAN>，或复制到启动时应在命令行标志中指定的另一个目录&nbsp;<SPAN style="FONT-FAMILY: courier new">-Dweblogic.typesDir=&lt;dir&gt;</SPAN> 中（8.1SP2 中新增）。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Troubleshoot_connection_problems></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>排除连接故障</B></U></FONT><BR>如果 Weblogic Server 无法连接到 LDAP 服务器，请<SPAN style="FONT-WEIGHT: bold">验证</SPAN>以下条件：<BR>
<UL>
<LI>LDAP 服务器已启动并正在运行。 </LI></UL>
<DIV style="MARGIN-LEFT: 40px">您可以尝试使用 LDAP 浏览器连接。有关详细信息，请参阅<A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Tools">工具</A>。</DIV>
<UL>
<LI>LDAP 服务器的主机名定义正确，并且被运行 WLS 的计算机识别。 </LI></UL>
<UL>
<LI>端口号正确（缺省值是 389，对于 SSL 是 636）。 </LI></UL>
<UL>
<LI>Principal 是 LDAP 中存在的一个用户，并且配置为用户的完整 DN，而不仅仅是用户 ID。Principal 可以类似于： </LI></UL>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">Principal="uid=admin, ou=Administrators, ou=TopologyManagement, o=NetscapeRoot"</SPAN><BR></DIV><BR>
<DIV style="MARGIN-LEFT: 40px">您可以使用 LDAP 浏览器验证用户的完整 DN。有关详细信息，请参阅<A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Tools">工具</A>。<BR></DIV>
<UL>
<LI>密码是 Principal 用户的密码。 </LI></UL>&nbsp;常见的连接错误代码是：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">LDAP error (49) - incorrect password (credentials)</SPAN><BR style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: courier new">LDAP error (32) - incorrect principal (user)</SPAN><BR></DIV><BR>有关 LDAP 结果代码的完整列表，请参阅 <A href="http://docs.sun.com/source/816-5608-10/log.htm#15324">http://docs.sun.com/source/816-5608-10/log.htm#15324</A>。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Troubleshoot_user_authentication_failure></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>排除用户身份验证故障</B></U></FONT><BR style="COLOR: rgb(0,0,0)">WLS 首先连接到 LDAP，然后根据在 Authentication Provider /自定义 Realm 中定义的用户基准 DN 和用户过滤器，尝试搜索用户。一旦找到该用户，它就会尝试用所提供的密码进行身份验证。<BR><BR>若要获得有关用户身份验证失败的确切位置的更多信息，可启用<A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Debug_Flags">调试标志</A>。<BR>此搜索将类似于：<BR></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: times new roman" color=#999999>&lt;SecurityDebug&gt;&lt;getDNForUser search ("ou=people,dc=beasys,dc=com", "(&amp;(uid=fred)(objectclass=person))", base DN &amp; below)</FONT><BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">如果用户不存在，您将会看到此消息：<BR></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999><SPAN style="FONT-FAMILY: times new roman">&lt;SecurityDebug&gt;&lt;returnConnection conn:netscape.ldap.LDAPConnection@e4bb3c&gt; javax.security.auth.login.FailedLoginException: [Security:090302]Authentication Failed: User fred denied</SPAN><BR></FONT></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">如果用户密码不正确，您将会看到下列信息：<BR></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: times new roman" color=#999999>&lt;SecurityDebug&gt; &lt;DN for user fred: uid=fred,ou=People,dc=beasys,dc=com&gt;</FONT><BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">下列信息说明在 LDAP 中找到了用户，但在身份验证时失败：<BR></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: times new roman" color=#999999>&lt;SecurityDebug&gt; &lt;authenticate user:fred with DN:uid=fred,ou=People,dc=beasys,dc=com&gt;<BR>&lt;Debug&gt; &lt;SecurityDebug&gt;&nbsp; &lt;<B>authentication failed 49</B>&gt;</FONT><BR></TD></TR></TBODY></TABLE>
<DIV align=left><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR></DIV>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><A name=Troubleshoot_group_membership_problems></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>排除组成员资格故障</B></U></FONT><BR style="COLOR: rgb(0,0,0)">在验证用户身份之后，就会对组进行一次搜索，以获得该用户所属组的列表，此列表可用于实现组和角色之间的角色映射。<BR><BR>这次搜索是使用在Authentication Provider中定义的“Static Group DNs from Member DN Filter”（来自成员 DN 过滤器的静态组 DN）来完成的。<BR>iPlanet 的示例：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="FONT-FAMILY: courier new">(&amp;(uniquemember=%M)(objectclass=groupofuniquenames))</FONT><BR></DIV><BR>可根据您的 LDAP 层次结构来更改此搜索。<BR><BR>用您正在各组中搜索的用户的完整 DN 代替 ％M。<BR><BR>为了获得组成员资格失败的确切位置的更多信息，可启用<A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Debug_Flags">调试标志</A>。<BR><BR>如果该用户不属于任何组，或者如果搜索标准是无效的，您将会看到类似如下的调试信息：<BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0)" color=#999999>&lt;SecurityDebug&gt;&nbsp; &lt;getDNForUser search("ou=people,dc=beasys,dc=com", "(&amp;(uid=fred)(objectclass=person))", base DN &amp; below)&gt;<BR>&lt;SecurityDebug&gt; &lt;DN for user fred: uid=fred,ou=People, dc=beasys,dc=com&gt;<BR>&lt;SecurityDebug&gt; &lt;search("ou=groups, dc=beasys,dc=com", "(&amp;(uniquemember=uid=fred,ou=People, dc=beasys,dc=com) (objectclass=groupofuniquenames))", base DN &amp; below)&gt;<BR>&lt;SecurityDebug&gt; &lt;Result has more elements: false&gt;</FONT><BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">注意上面如何用用户的完整 DN 代替 ％M。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Troubleshoot_role_mapping_failure></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>排除角色映射故障</B></U></FONT><BR>在进行用户身份验证并找到组之后，WLS 将用户 ＋ 组的列表与有权访问该资源的 Principal 用户进行比较，然后决定授予或拒绝访问权限。<BR><BR>在 WLS 7.x 和 WLS 8.x 中，执行对角色映射器的调用以获得用户/组拥有的角色，然后执行对 Authorization Provider 的调用，从而授予或拒绝对所请求资源的访问权限。<BR><BR>如果存在多个 Authorization Provider，则由 Adjudicator 根据授权访问决策来决定是否授予或拒绝对所请求资源的访问权限。<BR><BR>若要获得有关角色映射失败的确切位置的更多信息，可启用<A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#Debug_Flags">调试标志</A>。<BR><BR>下面是在 WLS 8.x 中对一个 Weblogic 用户进行调试输出的示例，该用户是 Admin 组的成员，但不拥有 Admin 角色，因此无权启动服务器：<BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0)" color=#999999>&lt;SecurityDebug&gt;&nbsp; &lt;Default RoleMapper getRoles(): input arguments:<BR>Subject: 2<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSUserImpl("weblogic")<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSGroupImpl("Admin")<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Resource: type=&lt;svr&gt;, application=, server=cgServer, action=boot&gt; <BR>&lt;SecurityDebug&gt; &lt;Default RoleMapper getRoles(): returning roles: <B>Anonymous</B>&gt; <BR>&lt;SecurityDebug&gt; &lt;RoleManager.getRoles&nbsp; Subject: Subject: 2<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSUserImpl("weblogic")<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSGroupImpl("Admin")<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Resource: &lt;svr&gt; type=&lt;svr&gt;, application=, server=cgServer, action=boot Anonymous roles.&gt; <BR>&lt;SecurityDebug&gt; &lt;Default Authorization isAccessAllowed(): input arguments:&gt; <BR>&lt;SecurityDebug&gt; &lt;&nbsp;&nbsp;&nbsp; Subject: 2<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSUserImpl("weblogic")<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Principal = class weblogic.security.principal.WLSGroupImpl("Admin")<BR>&lt;SecurityDebug&gt; &lt;Roles:Anonymous&gt; <BR>&lt;SecurityDebug&gt; &lt;Resource: type=&lt;svr&gt;, application=, server=cgServer, action=boot&gt; <BR>&lt;SecurityDebug&gt; &lt;Direction: ONCE&gt; <BR>&lt;SecurityDebug&gt; &lt;Context Handler: &gt; <BR>&lt;SecurityDebug&gt; &lt;null&gt; <BR>&lt;SecurityDebug&gt; &lt;<B>Default Authorization isAccessAllowed(): returning DENY</B>&gt;</FONT><BR></TD></TR></TBODY></TABLE>
<DIV align=left><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR></DIV>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><A name=How_to_get_Configuration_data></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>如何获得配置数据</B></U></FONT><BR><SPAN style="FONT-WEIGHT: bold">WLS 6.x</SPAN><BR>在 WLS 6.x 中，自定义安全 Realm 的配置信息保存到 <FONT style="FONT-FAMILY: courier new">config.xml</FONT> 中。<BR><BR><SPAN style="FONT-WEIGHT: bold">WLS 7.x</SPAN><BR>在 WLS 7.x 中，Authentication Provider 配置并未在 <SPAN style="FONT-FAMILY: courier new">config.xml</SPAN>&nbsp;中定义。该信息以二进制格式保存在 <SPAN style="FONT-FAMILY: courier new">domain_name\userConfig\Security</SPAN> 目录中。<BR><BR>若要获得配置信息，可在使用 <SPAN style="FONT-FAMILY: courier new">setEnv.cmd/sh</SPAN> 设置环境后，在域目录中执行下列命令：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">java weblogic.management.commo.WebLogicMBeanDumper -includeDefaults -name Security:* output-file</SPAN><BR></DIV><BR>此操作将会创建一个 xml 格式输出文件，其中将包含与 WLS 7.x 中的安全性相关的所有配置数据。<BR><BR>有关此命令的详细信息，请参阅：<A href="http://e-docs.bea.com/wls/docs70/admin_domain/failures.html#1110150">http://e-docsbea.com/wls/docs70/admin_domain/failures.html#1110150</A><BR><BR><SPAN style="FONT-WEIGHT: bold">WLS 8.x</SPAN><BR>在 WLS 8.x 中，Authentication Provider 的配置保存在 <SPAN style="FONT-FAMILY: courier new">config.xml</SPAN> 中。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=What_is_the_Control_Flag_in_Authentication_Provider></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>什么是 Authentication Provider 中的控制标志？</B></U></FONT><BR>在 WLS 7.x 和 WLS 8.x 中，可以配置多个 Authentication Provider。您必须使用 Authenticator--&gt;General 选项卡上的 JAAS Control Flag 属性来控制在登录序列中如何使用 Authentication Provider。<BR><BR>控制标志可取下面这些值之一：<BR>
<UL>
<LI>REQUIRED － 总是调用 Authentication Provider，并且用户必须始终通过其身份验证测试。 </LI></UL>
<UL>
<LI>SUFFICIENT － 如果用户通过了此 Authentication Provider 的身份验证测试，则不再执行其它 Authentication Provider（JAAS Control Flag 设置为 REQUIRED 的 Authentication Provider 除外），因为已对该用户进行了充分的身份验证。 </LI></UL>
<UL>
<LI>REQUISITE － 如果用户通过了此 Authentication Provider 的身份验证测试，执行其它 Authentication Provider 但可能会失败（JAAS Control Flag 设置为 REQUIRED 的 Authentication Provider 除外）。 </LI></UL>
<UL>
<LI>OPTIONAL － 允许该用户通过这些 Authentication Provider 的身份验证测试，但是，如果在安全 Realm 中配置的所有 Authentication Provider 都将 JAAS Control Flag 设置为 OPTIONAL，则该用户必须通过其中一个 Authentication Provider 的身份验证测试。 </LI></UL>备注：在创建安全 Provider 时，WebLogic Server 管理控制台实际上将 JAAS Control Flag 设置为 OPTIONAL。安全提供程序的 MBean 实际上缺省为 REQUIRED。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Enumeration_of_usersgroups_in_console></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B></B></U></FONT><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>在控制台中列举用户/组</B></U></FONT><BR>如果 ldap 中有过多的用户/组，并且从控制台中访问它们时服务器挂起或需要很多时间，则可以进行如下设置来禁止列举所有用户/组：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">EnumerationAllowed="false"</SPAN> （在 <FONT style="FONT-FAMILY: courier new">config.xml</FONT><FONT style="FONT-FAMILY: courier new"><SPAN style="FONT-FAMILY: times new roman"> 的 &lt;<SPAN style="FONT-FAMILY: courier new">Realm</SPAN></SPAN></FONT><SPAN style="FONT-FAMILY: times new roman">&gt; 标记上）。</SPAN><BR></DIV><BR>备注：这只是针对用于 WLS 6.x 和 WLS 7.x 兼容模式的最新 Service Pack。对于 WLS 8.x，要确保至少拥有 SP2，因为在 SP2 中修正了一些问题。<BR><BR><A name=Performance_Issues></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>性能问题</B></U></FONT><BR>用户身份验证的当前行为如下：<BR>
<OL>
<LI>在验证用户身份之后，确定组成员资格。 </LI></OL>
<OL start=2>
<LI>将用户所属的组确定作为一个列表。然后对于此列表中的每个组，查找该组所属的所有组。 </LI></OL>
<OL start=3>
<LI>这一过程递归执行，直到某个组不再属于其它组。 </LI></OL>例如，如果用户 U1 属于组 G1 和 G2，而组 G1 属于 G3，那么用户 U1 属于 G1、G2 和 G3。<BR>为 U1 确定组成员资格 － 获得 G1 和 G2，然后为 G1 确定组关系（获得 G3），然后为 G2 确定组关系（无结果）。<BR><BR>这种递归搜索可能存在性能问题，特别是在 LDAP 中存在很多组的时候。<BR><BR>为了避免这种性能问题，可以配置两个参数：<BR>
<UL>
<LI style="FONT-FAMILY: times new roman">GroupMembershipSearching 
<LI style="FONT-FAMILY: times new roman">MaxGroupMembershipSearchLevel </LI></UL><SPAN style="FONT-WEIGHT: bold">GroupMembershipSearching</SPAN><BR>
<DIV style="TEXT-ALIGN: left">此属性控制组搜索的深度是有限还是无限。缺省值是无限次搜索。如果配置了有限次搜索，则 <SPAN style="FONT-FAMILY: courier new">MaxGroupMembershipSearchLevel </SPAN>属性指定该限值。如果配置了无限次搜索，则忽略 <SPAN style="FONT-FAMILY: courier new">MaxGroupMembershipSearchLevel</SPAN>。<BR></DIV><BR><SPAN style="FONT-WEIGHT: bold">MaxGroupMembershipSearchLevel</SPAN><BR>如果已将 <SPAN style="FONT-FAMILY: courier new">GroupMembershipSearching</SPAN> 属性设置为有限制，则此属性控制组成员资格搜索的深度。否则忽略此属性。缺省值是“0”（数字零）。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Embedded_LDAP_issues></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>内嵌的 LDAP 问题</B></U></FONT><BR>在<FONT style="FONT-FAMILY: courier new"> domain/server/ldap/ldapfiles</FONT> 文件夹下创建了一个名为 <FONT style="FONT-FAMILY: courier new">embeddedldap.lok</FONT> 的文件。<BR>如果该文件存在，则服务器不能启动，并将看到下列错误：<BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; BACKGROUND-COLOR: rgb(204,204,204)"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: times new roman" color=#999999>&lt;Emergency&gt; &lt;WebLogicServer&gt; &lt;BEA-000342&gt; <BR>&lt;Unable to initialize the server: weblogic.server.ServiceFailureException: <BR>Could not obtain an exclusive lock to the embedded LDAP data files directory: <BR>./server1/ldap/ldapfiles because another WebLogic Server is already using this directory. Ensure that the first WebLogic Server is completely shutdown and restart the server.&gt;</FONT><BR></TD></TR></TBODY></TABLE><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">为避免此错误，应确保没有没有启动其它进程或访问此文件并将它删除。<BR>在关机时通常会对此文件进行解锁，但如果上次发生了异常关机（如崩溃），则可能未正确解锁此文件，从而在下次启动时产生此类错误。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Debug_Flags></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>调试标志</B></U></FONT><BR>在 WLS 6.x 中，在 <SPAN style="FONT-FAMILY: courier new">config.xml</SPAN> 的 <FONT style="FONT-FAMILY: courier new">ServerDebug</FONT> 标记下，添加下列信息以设置调试标志：<BR><BR>&nbsp;&nbsp;&nbsp; &nbsp;<FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new">&nbsp;&nbsp; </FONT><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&lt;ServerDebug&nbsp; DebugSecurityRealm="true" Name="myserver"/&gt;</FONT><BR><BR>在 WLS 7.x 和 WLS 8.x 中，在 <SPAN style="FONT-FAMILY: courier new">config.xml</SPAN> 的 <SPAN style="FONT-FAMILY: courier new">ServerDebug</SPAN> 标记下，添加下列信息以设置调试标志。服务器重新启动后将考虑这些标志：<BR>&nbsp;<BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&lt;ServerDebug </FONT><BR><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&nbsp;&nbsp;&nbsp; DebugSecurityAdjudicator="true"&nbsp;&nbsp;&nbsp; // for security adjudicator debug</FONT><BR><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&nbsp;&nbsp;&nbsp; DebugSecurityAtn="true" &nbsp;&nbsp;&nbsp; &nbsp; // for security authentication debug</FONT><BR><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&nbsp;&nbsp;&nbsp; DebugSecurityAtz="true"&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; // for security authorization debug</FONT><BR><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>&nbsp;&nbsp;&nbsp; DebugSecurityRoleMap="true" &nbsp;&nbsp;&nbsp; &nbsp;// for security role mapping debug </FONT><BR><FONT style="COLOR: rgb(0,0,0); FONT-FAMILY: courier new" color=#999999>Name="MyServer"/&gt;</FONT><BR></DIV><BR>也可以通过命令行，用 <FONT style="FONT-FAMILY: courier new">weblogic.Admin SET</FONT> 命令来设置 <SPAN style="FONT-FAMILY: courier new">ServerDebugMBean</SPAN>。注意，尽管调试属性的变化值将反映在 <FONT style="FONT-FAMILY: courier new">ServerDebugMBean</FONT> 状态中，但在重新启动 Admin 服务器之前将不再产生输出。<BR><BR>下例说明如何使用命令行界面动态地启用 <SPAN style="FONT-FAMILY: courier new">DebugSecurityAtz</SPAN> 调试属性：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">java weblogic.Admin -url t3://host:port -username adminuser -password adminpwd SET -type ServerDebug -property DebugSecurityAtz true<BR><BR></SPAN></DIV>此命令将在配置中设置所有服务器实例上的 <SPAN style="FONT-FAMILY: courier new">DebugSecurityAtz</SPAN> 调试属性。<BR><BR>通过检查 <SPAN style="FONT-FAMILY: courier new">config.xml</SPAN> 中的 <FONT style="FONT-FAMILY: courier new">StdoutDebugEnabled</FONT> 被设置为“true”，确保已启用调试到 <SPAN style="FONT-FAMILY: courier new">stdout </SPAN>选项。可以通过控制台或命令行来启用调试到 <SPAN style="FONT-FAMILY: courier new">stdout </SPAN>选项：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN style="FONT-FAMILY: courier new">-Dweblogic.StdoutDebugEnabled=true</SPAN><BR></DIV><BR>调试信息将被记录到服务器日志以及标准输出中。<BR><BR>在 WLS 8.1 中，服务器日志文件在启动时用 BEA-161519 消息进行指定：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="FONT-FAMILY: courier new">BEA-161519 Notice: The server log file fileName is opened. All server side log events will be written to this file.</FONT><BR></DIV><BR>或者在 WLS 7.x 中通过以下消息来指定：<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><FONT style="FONT-FAMILY: courier new">FileLogger Opened at fileName.</FONT><BR></DIV><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR><A name=Tools></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>工具</B></U></FONT><BR><SPAN style="FONT-WEIGHT: bold">LDAP 浏览器</SPAN><BR>LDAP 浏览器可用于访问内嵌 LDAP 或其它任何 LDAP。您可以从网址 <A href="http://www.iit.edu/%7Egawojar/ldap/">http://www.iit.edu/~gawojar/ldap/</A> 下载此工具。<BR><BR>若要确认您的 LDAP 服务器已启动并正在运行，可以尝试用此 LDAP 浏览器连接到该服务器和端口号。<BR><BR>单击“Fetch DNs”将显示 LDAP 服务器上的所有可用根基准 DN：<BR></TD></TR></TBODY></TABLE><BR><IMG height=211 alt=EditSession src="http://support.bea.com/es_static/images/editSessionImgNew.bmp" width=355><BR><BR><IMG height=212 alt="Fetch DN" src="http://support.bea.com/es_static/images/editSessionImg2New.bmp" width=356><BR><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">一旦连接后，您就可以看到 LDAP 树：<BR></TD></TR></TBODY></TABLE><BR><IMG height=302 alt=LDAPTree src="http://support.bea.com/es_static/images/LdapbrowserNew.bmp" width=451><BR><BR>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top">正如在文件系统内用文件路径来唯一标识文件一样，在目录树内用辨别名 (DN) 来唯一标识目录条目。DN 通过用一系列以逗号分隔的属性和属性值来标识条目。但是，DN 的路径规范与传统的文件系统的顺序相反。也就是说，文件系统一般用最右边的分量中指定的文件的实际名称来从左至右跟踪通向文件的路径，而 DN 指定最左边的分量作为实际目录对象，最右边的值作为目录根点。<BR><BR><SPAN style="FONT-WEIGHT: bold">ldapsearch 命令</SPAN><BR>搜索用户的另一个方法是使用 ldapsearch 命令，例如：<BR><BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<SPAN style="FONT-FAMILY: courier new"> ldapsearch b "dc=beasys,dc=com" uid=fred</SPAN><BR><BR>有关此命令和使用方法的详细信息，请参阅 <A href="http://docs.sun.com/source/816-5608-10/utilities.htm#2019555">http://docs.sun.com/source/816-5608-10/utilities.htm#2019555</A>。<BR><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR></TD></TR></TBODY></TABLE>
<TABLE style="WIDTH: 615px; TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><A name=Examples_of_Authentication_Providers></A><FONT style="COLOR: rgb(0,0,0)" color=#009900><U><B>Authentication Provider 的示例</B></U></FONT><BR><BR><B><A name=IPlanet_Authenticator_Example></A>IPlanet 身份验证程序示例</B><BR><BR><FONT style="FONT-FAMILY: courier new">&lt;weblogic.security.providers.authentication.IPlanetAuthenticator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;ControlFlag="SUFFICIENT"&nbsp;&nbsp;&nbsp; <BR>&nbsp;Credential="{3DES}eFlqRhsYDyU5sqpnDRzCjg=="&nbsp;&nbsp; <BR>&nbsp;DynamicGroupNameAttribute="cn"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;DynamicGroupObjectClass="groupofURLs"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;DynamicMemberURLAttribute="memberURL"&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;GroupBaseDN="ou=groups, dc=example,dc=com"<BR>&nbsp;GroupFromNameFilter="(|(&amp;amp;(cn=%g)(objectclass=groupofUniqueNames))(&amp;amp;(cn=%g)(objectclass=groupOfURLs)))"&nbsp;&nbsp; <BR>&nbsp;Host="HOST IP or NAME OF LDAP"&nbsp;&nbsp; <BR>&nbsp;Name="Security:Name=myrealmIPlanetAuthenticator"<BR>&nbsp;Port="the port number of LDAP"<BR>&nbsp;Principal="uid=admin, ou=Administrators, ou=TopologyManagement, o=NetscapeRoot"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;Realm="Security:Name=myrealm"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;StaticGroupDNsfromMemberDNFilter="(&amp;amp;(uniquemember=%M)(objectclass=groupofuniquenames))"&nbsp; <BR>&nbsp;StaticGroupNameAttribute="cn"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;StaticGroupObjectClass="groupofuniquenames"&nbsp;&nbsp;&nbsp; <BR>&nbsp;StaticMemberDNAttribute="member"&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;UserBaseDN="ou=people,dc=example,dc=com"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;UserFromNameFilter="(&amp;amp;(uid=%u)(objectclass=person))"&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;UserNameAttribute="uid"<BR>&nbsp;UserObjectClass="person"/&gt;</FONT><BR><BR><B><A name=Active_Directory_Authenticator_Example></A>Active Directory 身份验证程序示例</B><BR><BR><FONT style="FONT-FAMILY: courier new">&lt;weblogic.security.providers.authentication.ActiveDirectoryAuthenticator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; ControlFlag="SUFFICIENT" Credential="{3DES}96Kl0euDFQQ="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; GroupBaseDN="CN=Users,DC=supportLDAP,DC=example,DC=com"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; GroupFromNameFilter="(&amp;amp;(cn=%g)(objectclass=group))"&nbsp;&nbsp; <BR>&nbsp; Host=" HOST IP or NAME OF LDAP"&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<BR>&nbsp; Name="Security:Name=myrealmActiveDirectoryAuthenticator"<BR>&nbsp; Principal="CN=Administrator,CN=Users,DC=supportLDAP,DC=example,DC=com&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; Realm="Security:Name=myrealm"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; StaticGroupDNsfromMemberDNFilter="(&amp;amp;(member=%M)(objectclass=group))"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; StaticGroupNameAttribute="cn" <BR>&nbsp; StaticGroupObjectClass="group"<BR>&nbsp; StaticMemberDNAttribute="member"<BR>&nbsp; UserBaseDN="CN=Users,DC=supportLDAP,DC=example,DC=com"<BR>&nbsp; UserFromNameFilter="(&amp;amp;(cn=%u)(objectclass=user))"<BR>&nbsp; UserNameAttribute="cn" UserObjectClass="user"/&gt;</FONT><BR><BR>&nbsp;<B><A name=OpenLDAP_Authenticator_Example></A>OpenLDAP 身份验证程序示例</B><BR><BR><FONT style="FONT-FAMILY: courier new">&lt;weblogic.security.providers.authentication.OpenLDAPAuthenticator<BR>&nbsp;ControlFlag="SUFFICIENT" Credential="{3DES}96Kl0euDFQQ="<BR>&nbsp;GroupBaseDN="ou=groups, dc=example, dc=com"<BR>&nbsp;GroupFromNameFilter="(&amp;amp;(cn=%g)(objectclass=groupofnames))"<BR>&nbsp;Name="Security:Name=myrealmOpenLDAPAuthenticator"<BR>&nbsp;Principal="cn=Directory Manager,dc=example,dc=com"<BR>&nbsp;Realm="Security:Name=myrealm"<BR>&nbsp;StaticGroupDNsfromMemberDNFilter="(&amp;amp;(member=%M)(objectclass=groupofnames))"<BR>&nbsp;StaticGroupNameAttribute="cn"<BR>&nbsp;StaticGroupObjectClass="groupofnames"<BR>&nbsp;StaticMemberDNAttribute="member"<BR>&nbsp;UserBaseDN="ou=people, dc=example, dc=com"<BR>&nbsp;UserFromNameFilter="(&amp;amp;(cn=%u)(objectclass=person))"<BR>&nbsp;UserNameAttribute="cn" UserObjectClass="person"/&gt;</FONT><BR></TD></TR></TBODY></TABLE>
<DIV align=left><BR><A href="http://www.bea.com.cn/support_pattern/LDAP_Issues_Pattern.html#TOP">返回页首</A><BR><BR></DIV>
<TABLE style="WIDTH: 615px; COLOR: rgb(0,0,0); TEXT-ALIGN: left" cellSpacing=2 cellPadding=2>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top"><SPAN style="TEXT-DECORATION: underline"><SPAN style="FONT-WEIGHT: bold">是否需要更多帮助？</SPAN></SPAN><BR><SPAN>如果您已经理解这个模式，但仍需要其它帮助，您可以：<BR></SPAN>
<OL>
<LI><SPAN>在 </SPAN><SPAN><A href="http://support.bea.com/">http://support.bea.com/</A></SPAN><SPAN> </SPAN><SPAN>上查询 AskBEA（例如使用</SPAN><SPAN>“ldap issue”)，以查找其它已发布的解决方案。</SPAN> 
<LI><SPAN>在 </SPAN><SPAN><A href="http://support.bea.com/">http://support.bea.com/</A></SPAN><SPAN></SPAN> 上，向 BEA 的某个新闻组中提出更详细具体的问题。<BR></LI></OL>如果这还不能解决您的问题，并且您拥有有效的技术支持合同，您可以通过登录以下网站来打开支持案例：<SPAN><A href="http://support.bea.com/">http://support.bea.com/</A></SPAN>。</TD></TR></TBODY></TABLE><BR>
<TABLE width=615 border=1>
<TBODY>
<TR>
<TD>
<P><STRONG>反馈</STRONG></P>
<P><FONT color=#000000>请给我们提供您的意见，说明此支持诊断模式<STRONG>“LDAP 问题”</STRONG>一文是否有所帮助，您需要的任何解释，以及对<A href="mailto:support.ke@bea.com?subject=Patterns%20Feedback:%20LDAP%20Issues&amp;body=">支持诊断模式</A>的新主题的任何要求。<BR></FONT></P></TD></TR></TBODY></TABLE><BR>
<TABLE width=615 border=1><!--DWLayoutTable-->
<TBODY>
<TR>
<TD>
<P><STRONG>免责声明：</STRONG></P>
<P>依据 BEA 与您签署的维护和支持协议条款，BEA Systems, Inc. 在本网站上提供技术技巧和修补程序供您使用。虽然您可以将这些信息和代码与您获得 BEA 授权的软件一起使用，但 BEA 并不对所提供的技术技巧和修补程序做任何形式的担保，无论是明确的还是隐含的。</P>
<P>本文档中引用的任何商标是其各自所有者的财产。有关完整的商标信息，请参考您的产品手册。</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/4080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-08 13:35 <a href="http://www.blogjava.net/kapok/archive/2005/05/08/4080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高级页面流（Page flow）：嵌套、异常处理和 Global.app </title><link>http://www.blogjava.net/kapok/archive/2005/05/08/4079.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/08/4079.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4079.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/08/4079.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4079.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4079.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/wlportal/20031029.html">http://dev2dev.bea.com.cn/techdoc/wlportal/20031029.html</A><BR><BR>基本页面流框架能够为Web项目的一组页面集中管理导航状态和逻辑。尽管这比以往的Web开发模式有了明显的进步，但随着导航逻辑遍布各个页面、状态被存储在众多会话对象当中，某些更高级的页面流特性可以使您的项目更加出色和强大。 
<P>本文假定读者熟悉如何在WebLogic Workshop中构建和运行页面流。文中将会介绍三个特性：嵌套、声明性异常处理、Global.app。</P>
<P><B>嵌套</B><BR></P>
<P>默认情况下，在一个新页面流中执行动作会导致当前页面流失效，这使得您可以为项目的不同部分创建独立的控制器，并最小化了每次用户会话需要保存的数据量。每个页面流管理它自己的状态和逻辑。在WebLogic Workshop IDE中，将控制权交给另外一个页面流用一个代表外部页面流的暗淡的终端节点表示。<BR><IMG height=82 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image001.jpg" width=500> <BR><BR>想要更深入地了解高级页面流？<A href="ftp://edownload:BUY_ME@ftpna2.bea.com/pub/downloads/advanced_jpf.zip">请下载</A> 本文附带的作者示例应用。 <BR>页面流嵌套使开发人员可以更好地将项目分解成独立的、自包含的功能块。它的核心功能是把当前页面流暂时放在一边，并将控制权交给另外一个页面流，该页面流将来会返回到原来的页面流。</P>
<P></P>
<P>什么时候应该使用页面流嵌套呢？当执行以下任务时嵌套十分有用：</P>
<UL>
<LI>从用户那里搜集用于当前页面流的数据 
<LI>允许用户改正错误，或在执行特定动作的过程中提供额外信息 
<LI>为当前页面流中的数据提供另外一种显示视图 
<LI>显示在当前页面流中有用的用户信息（例如，帮助窗口） </LI></UL>让我们从一个简单的例子开始。在这个场景中，用户被要求选择一种颜色并根据选定的颜色被重定向到不同的页面。如果没有嵌套，页面流(/noNesting/noNestingController.jpf)可能是这样的： 
<P><IMG height=146 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image002.jpg" width=458> <BR>如果颜色选择的部分更为复杂（比如在继续之前需要一个确认过程），就可以采用一个嵌套页面流来替代chooseColor.jsp，它同样可以满足要求（位于/simpleNesting/simpleNestingController.jpf）：<BR><IMG height=156 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image003.jpg" width=487> <BR>嵌套页面流的一个重要特性是它可以替代页面的许多工作。嵌套页面流可以启动动作，甚至可以进行“post”，或者用表单进行初始化。通常，总是可以用嵌套页面流来替代页面。以下是该嵌套页面流的流程视图/chooseColor/chooseColorController.jpf:<BR><IMG height=197 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image004.jpg" width=500> <BR>该页面流允许用户选择一种颜色，请求确认，然后在原来的页面流上启动一个动作。该页面流出于激活状态时，原来的页面流被保存在用户会话中（位于堆栈中）。当该页面流结束时，被保存的原页面流上的“chooseRed”或“chooseBlue”动作将被激活。 </P>
<P>创建和使用嵌套页面流是相当简单的。页面流向导提供了一个“make this a nested page flow”选项，它可以用类一级的注解@jpf:controller将新页面流定义为嵌套页面流：<BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:controller nested="true"</P>
<P>*/</P>
<P>public class chooseColorController extends PageFlowController<BR></P></TD></TR></TBODY></TABLE>
<P>想让嵌套页面流在原页面流上启动一个动作，您可以定向到一个“退出节点”（页面流面板上的红色方块“Exit”），它的动作方法形如下面所示： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action</P>
<P>* @jpf:forward name="red" return-action="chooseRed"</P>
<P>* @jpf:forward name="blue" return-action="chooseBlue"</P>
<P>*/</P>
<P>public Forward done(ChooseColorForm form)</P>
<P>{</P>
<P>　if ( form.getChosenColor().equals( "Red" ) )</P>
<P>　{</P>
<P>　return new Forward( "red" );</P>
<P>　}</P>
<P>　else</P>
<P>　{</P>
<P>　return new Forward( "blue" );</P>
<P>　}</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>定向到@jpf:forward ——它定义了一个return-action而不是path——时，嵌套页面流就会退出。 </P>
<P>下面介绍一个稍微复杂的例子。在/demoNesting/demoNestingController.jpf中，用户进入一个嵌套页面流（/chooseAirport/chooseAirportController.jpf），该页面流是帮助用户查找机场的向导。嵌套页面流将选定的机场返回（或者说“post”）到原页面流中，原页面流继续执行。<BR></P><IMG height=375 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image005.jpg" width=391> <BR>该页面流展示了与嵌套相关的两个新特性： 
<P>·如果嵌套页面流激活一个“chooseAirportCancelled”动作，页面流将会返回到刚才展示给用户的前一页面。这是通过在@jpf:forward中使用return-to属性达到的： </P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/* 
<P>* @jpf:action</P>
<P>* @jpf:forward name="previousPage" return-to="page"</P>
<P>*/</P>
<P>protected Forward chooseAirportCancelled()</P>
<P>{</P>
<P>return new Forward( "previousPage" );</P>
<P>} </P></TD></TR></TBODY></TABLE>
<P>·当启动“chooseAirportDone”动作时，嵌套页面流会返回一个表单（FormData 类）。这些将在后面讨论，但值得注意的是：页面流处理该返回表单的方式与它处理页面post过来的表单的方式一样。 <BR></P>
<P>嵌套页面流如下，/chooseAirport/chooseAirportController.jpf：<BR><IMG height=289 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image006.jpg" width=455> <BR>唯一不同的地方在于：在原页面流中启动“chooseAirportDone”动作时将会同时返回一个表单。这是通过在@jpf:forward中使用return-form 属性实现的： </P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/* 
<P>* @jpf:action</P>
<P>* @jpf:forward name="done"</P>
<P>* return-action="chooseAirportDone"</P>
<P>* return-form="_currentResults"</P>
<P>*/</P>
<P>protected Forward confirmResults()</P>
<P>{</P>
<P>return new Forward( "done" );</P>
<P>}<BR></P></TD></TR></TBODY></TABLE><BR>在本例中，_currentResults 是嵌套页面流的一个成员变量。或者，如果您想要在本地初始化一个表单（避免使用成员变量），可以用return-form-type 属性声明表单类型，并将表单加入到返回的Forward 对象上： <BR>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/* 
<P>* @jpf:action</P>
<P>* @jpf:forward name="done"</P>
<P>* return-action="chooseAirportDone"</P>
<P>* return-form-type="ChooseAirportForm"</P>
<P>*/</P>
<P>protected Forward confirmResults( ConfirmationForm confirmForm )</P>
<P>{</P>
<P>ChooseAirportForm returnForm = new ChooseAirportForm( ?);</P>
<P>return new Forward( "done", returnForm );</P>
<P>} </P></TD></TR></TBODY></TABLE>
<P>除了@jpf:controller nested="true" 声明、激活退出动作和返回表单的模式之外，声明嵌套页面流与声明非嵌套页面流一样。</P>
<P></P>
<P><B>其它注意事项</B></P>
<UL>
<LI>定向/重定向到一个嵌套页面流或在其上启动任何动作都会引发嵌套。原页面流被保存到“嵌套堆栈”中，直到嵌套页面流在@jpf:forward中使用return-action 属性来启动一个返回动作。 
<LI>页面流可以自嵌套 
<LI>在嵌套页面流的动作方法中，您可以通过调用PageFlowUtils.getNestingPageFlow( getRequest() ) 来获得原页面流的一个引用； <BR>
<LI>嵌套页面流（任何其它页面流也是这样）可以在begin动作方法中接收一个FormData类型的参数。 <BR></LI></UL>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action</P>
<P>* @jpf:forward name="firstPage" path="index.jsp"</P>
<P>*/</P>
<P>protected Forward begin( InitForm form )</P>
<P>{</P>
<P>?</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>为了初始化嵌套页面流，原页面流可能会像下面这样定向到嵌套页面流： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action</P>
<P>* @jpf:forward name="nest" path="/nested/nestedController.jpf"</P>
<P>*/</P>
<P>protected Forward goNested()</P>
<P>{</P>
<P>nested.nestedController.InitForm initForm</P>
<P>= new nested.nestedController.InitForm();</P>
<P>initForm.setValue( ?);</P>
<P>return new Forward( "nest", initForm );</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<UL>
<LI>嵌套页面流实际定义将要返回的表单（FormData 类）。这和页面的行为不同，后者只是简单地将数据作为request参数post过去，这些参数被添加（或诱骗到）到与将要启动的动作相关联的表单bean中。更为精确的嵌套页面流return-form行为可以嵌套两个启动同一动作的页面流。处理来自两个嵌套页面的动作类似下面这样： <BR></LI></UL>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action</P>
<P>* @jpf:forward ?</P>
<P>*/</P>
<P>public Forward done( nested1.nested1Controller.OutputForm form )</P>
<P>{</P>
<P>?</P>
<P>}</P>
<P></P>
<P>/**</P>
<P>* @jpf:action</P>
<P>* @jpf:forward ?</P>
<P>*/</P>
<P>public Forward done( nested2.nested2Controller.DoneForm form )</P>
<P>{</P>
<P>?</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P><B>异常处理</B> </P>
<P>尽管可以在动作方法中通过编程来处理异常，但为了仅通过声明就可以处理异常，页面流还提供了一种更强大的框架。在动作方法和类级别上，处理异常的方式可以是直接定向到一个页面（或一个嵌套页面流），也可以是调用一个异常处理方法，由该方法决定下一步显示的页面。 </P>
<P>直接定向到一个URI是处理异常最简单的方法：<BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:catch type="Exception" path="/error.jsp"</P>
<P>* @jpf:catch type="NotLoggedInException"</P>
<P>* path="/login/loginController.jpf"</P>
<P>*/</P>
<P>public class exampleController extends PageFlowController </P></TD></TR></TBODY></TABLE>
<P>在上面的例子中，当一个NotLoggedInException 异常被抛出时，用户将看到/login/loginController.jsp。当其它异常被抛出时，用户看到/error.jsp （注意：页面流异常处理总是试图先匹配那些最具体的异常）。异常自动被存储在request的<NETUI:EXCEPTIONS> JSP 标签中，该标签可以显示异常信息和/或堆栈跟踪记录。 <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc><?XML:NAMESPACE PREFIX = NETUI /><NETUI:EXCEPTIONS showStackTrace="false" showMessage="true"></NETUI:EXCEPTIONS></TD></TR></TBODY></TABLE>
<P>也可以用异常处理方法来处理异常。为此，请在@jpf:catch中使用method属性： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>* @jpf:catch type="ExampleException" 
<P>* method="handleExampleException"</P>
<P>* message-key="myCustomMessage"<BR></P></TD></TR></TBODY></TABLE>
<P>method属性指向一个异常处理方法，message-key 属性用来解析资源包中的某条信息，该资源包是通过@jpf:message-resources 标签（该标签负责在/WEB-INF/classes/exceptions/Messages.properties中查找资源）在类级别上声明的：<BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>* @jpf:message-resources resources="exceptions.Messages" </TD></TR></TBODY></TABLE>
<P>异常处理方法本身被定义成下面这样： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:exception-handler</P>
<P>* @jpf:forward name="errorDetailsPage" path="errorDetails.jsp"</P>
<P>*/</P>
<P>protected Forward handleExampleException( ExampleException ex,</P>
<P>String actionName,</P>
<P>String message,</P>
<P>FormData form )</P>
<P>{</P>
<P>getRequest().setAttribute( "exceptionType",</P>
<P>ex.getClass().getName() );</P>
<P>getRequest().setAttribute( "customMessage", message );</P>
<P>return new Forward( "errorDetailsPage" );</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>在本例中，该方法在request中保存异常类型和自定义信息（在@jpf:catch中通过message-key 属性定义），这样errorDetails.jsp就能够将它们显示出来。</P>
<P></P>
<P><BR>Global.app </P>
<P>定义在/WEB-INF/src/global/Global.app的Global.app既是那些无法处理的动作和异常的最后处理者，也是保存会话范围状态的地方。自从任何一个页面流被请求开始，该类的一个唯一实例就被保存在用户会话中，并且一直存在直到会话结束。</P>
<P>在一个页面流中启动一个动作，但该页面流并不能处理这个动作，这时Global.app就会处理它。对于异常也是一样：如果异常在某个页面流中无法处理，Global.app将会处理它。下面的Global.app例子处理任何无法捕捉的Exception，并管理所有不由页面流处理的“goHome”动作。</P>
<P></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:catch type="Exception" path="/error.jsp"</P>
<P>*/</P>
<P>public class Global extends GlobalApp</P>
<P>{</P>
<P>/**</P>
<P>* @jpf:action</P>
<P>* @jpf:forward name="homePage" path="/index.jsp"</P>
<P>*/</P>
<P>public Forward goHome()</P>
<P>{</P>
<P>return new Forward( "homePage" );</P>
<P>}</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>如前所述，Global.app实例在整个用户会话中保持激活状态。想要从页面流中访问它的实例，需要做下面两件事情：</P>
<UL>
<LI>在页面流中声明一个特殊成员变量：protected global.Global globalApp。它会被自动初始化为当前Global.app实例，并可以在页面流的动作方法和生命周期方法（比如onCreate，beforeAction，afterAction）中使用。 <BR>
<LI>调用PageFlowUtils.getGlobalApp( getRequest() ); </LI></UL>
<P><BR>可以使用“globalApp”数据绑定上下文从JSP中访问Global.app公共成员以及getter/setter方法：<BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc><NETUI:LABEL value="{globalApp.someProperty}"></NETUI:LABEL></TD></TR></TBODY></TABLE>
<P><B>一个高级例子 </B></P>
<P>下面的例子用来演示嵌套、异常处理、和Global.app。在例子中，用户可以执行一个需要登录的动作（“doit”）。如果用户登录，页面流继续执行“doit”动作。否则一个异常将被迫抛出并被Global.app捕获，然后它将定向到一个嵌套登录页面流，在用户登录之后再重新返回“doit”动作。首先，我们来看看主页面流（/demoLogin/demoLoginController.jpf）：<BR></P>
<P><IMG height=316 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image007.jpg" width=289> <BR>该页面流中唯一特殊的地方就是“doit”动作的行为： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action login-required="true"</P>
<P>* @jpf:forward name="success" path="success.jsp"</P>
<P>*/</P>
<P>public Forward doit()</P>
<P>{</P>
<P>return new Forward( "success" );</P>
<P>} </P></TD></TR></TBODY></TABLE>
<P>@jpf:action 中的login-required 属性在用户没有登录的情况下将会抛出一个NotLoggedInException 。这从逻辑上相当于明确地抛出异常： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:action</P>
<P>* @jpf:forward name="success" path="success.jsp"</P>
<P>*/</P>
<P>public Forward doit()</P>
<P>throws NotLoggedInException</P>
<P>{</P>
<P>if ( getRequest().getUserPrincipal() == null )</P>
<P>{</P>
<P>throw new NotLoggedInException();</P>
<P>}</P>
<P></P>
<P>return new Forward( "success" );</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>当从doit中抛出NotLoggedInException时，该异常将被Global.app捕获，它将定向到嵌套页面流/login/loginController.jpf： <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:catch type="NotLoggedInException"</P>
<P>* path="/login/loginController.jpf"</P>
<P>*/</P>
<P>public class Global extends GlobalApp </P></TD></TR></TBODY></TABLE>
<P>/login/loginController.jpf 如下：<BR><IMG height=270 src="http://dev2dev.bea.com.cn/images/article/portal031013_03/image008.jpg" width=493> <BR>当嵌套页面流启动“loginOK”动作时，它不被/demoLogin/demoLoginController.jpf 处理（仍是原页面流），而是被交给Global.app，Global.app在当前页面流——/demoLogin/demoLoginController.jpf——中重新运行先前的动作。 <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:catch type="NotLoggedInException"</P>
<P>* path="/login/loginController.jpf"</P>
<P>*/</P>
<P>public class Global extends GlobalApp</P>
<P>{</P>
<P>/**</P>
<P>* @jpf:action</P>
<P>* @jpf:forward name="previousAction" return-to="action"</P>
<P>*/</P>
<P>protected Forward loginOK()</P>
<P>{</P>
<P>return new Forward( "previousAction" );</P>
<P>}<BR></P></TD></TR></TBODY></TABLE>
<P>@jpf:forward 中的return-to="action" 属性使最近执行过的动作被重新运行；在本例中即是“doit”，现在它可以成功执行了。最后，如果嵌套页面流启动“loginCancel”动作，同样将会交给Global.app处理，Global.app将返回到当前页面流的前一个显示页面（仍然在/demoLogin/demoLoginController.jpf 中）。 <BR></P>
<TABLE cellSpacing=3 cellPadding=3 width="98%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>/** 
<P>* @jpf:catch type="NotLoggedInException"</P>
<P>* path="/login/loginController.jpf"</P>
<P>*/</P>
<P>public class Global extends GlobalApp</P>
<P>{</P>
<P>/**</P>
<P>* @jpf:action</P>
<P>* @jpf:forward name="previousAction" return-to="action"</P>
<P>*/</P>
<P>protected Forward loginOK()</P>
<P>{</P>
<P>return new Forward( "previousAction" );</P>
<P>}</P>
<P></P>
<P>/**</P>
<P>* @jpf:action</P>
<P>* @jpf:forward name="previousPage" return-to="page"</P>
<P>*/</P>
<P>protected Forward loginCancel()</P>
<P>{</P>
<P>return new Forward( "previousPage" );</P>
<P>}</P>
<P>}</P></TD></TR></TBODY></TABLE>
<P>在本例中，如果用户取消登录，他将重新回到起始页面。 </P></NETUI:EXCEPTIONS><img src ="http://www.blogjava.net/kapok/aggbug/4079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-08 10:59 <a href="http://www.blogjava.net/kapok/archive/2005/05/08/4079.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用于BEA WebLogic Workshop的Documentum Business Objects </title><link>http://www.blogjava.net/kapok/archive/2005/05/07/4068.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sat, 07 May 2005 10:17:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/07/4068.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4068.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/07/4068.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4068.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4068.html</trackback:ping><description><![CDATA[<A href="http://dev2dev.bea.com.cn/techdoc/wlworkshop/2004102802.html">http://dev2dev.bea.com.cn/techdoc/wlworkshop/2004102802.html</A><BR><BR>本系列的文章，从Documentum业务对象框架（Documentum Business Object Framework，BOF）开始，介绍了用于BEA WebLogic Workshop的Documentum 业务对象控件(Documentum Business Objects Control，DBOC)。
<P>　　本文是第2部分，介绍了一些实现上的底层支持技术，讨论与Documentum安全性的集成，并研究一些示例代码。这篇文章是在第一篇文章（可从Ness的Web站点上得到）所介绍的概念基础上形成的。</P>
<P>　　作为Documentum和BEA的合作伙伴，Ness科技受邀在 BEA的Workshop和Documentum基于服务的业务对象之间建立集成。这一集成包括控件、示例应用程序和集成的帮助，所有这些都可以在第一次使用控件时下载并安装到Workshop中。</P><BR><BR>
<P><B>　　DBOC是怎样集成到Workshop中的？</B><BR>　　WebLogic Workshop引入了Java控件，把它作为一种封装业务逻辑、方便地访问企业资源的方法。为Workshop集成开发环境（IDE）提供的内置平台控件，可以连接数据库、EJB、消息队列等。</P>
<P>　　因为BEA致力于推广控件，所以他们使应用程序开发人员可以容易地建立自己的自定义控件，代表应用程序的具体逻辑，并为开发团队提供一套一致的工具。 </P>
<P>　　为了进一步推动Java控件的创新，Workshop的8.12版本引入了一个扩展开发工具包，支持第三方控件和用户界面组件无缝地集成入WLW环境。DBOC就是这一新的扩展性框架的第一个实现。</P>
<P>　　我们会稍花一点时间来讨论这是如何办到的，因为大家可能想做类似的集成工作，把自己的自定义控件集成进IDE，其中可以包含利用DBOC的控件，但是当然不仅限于此。</P>
<P>　　在第一次安装WebLogic Platform 8.12版本时，在BEAHOME\ext_components文件夹里会放入一个 DocumentumBusinessObjectsControlStub.zip文件。</P>
<P>　　这个小小的压缩包里包含有关控件的足够信息（文本、图标、下载URL），所以它可以在Workshop的增加控件菜单里显示。</P>
<P>　　<IMG height=164 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102801.gif" width=244></P>
<P></P>
<P>　　在准备第一次使用DBOC时， Workshop 会提示它还没有安装，然后会下载完整的控件档案压缩包DocumentumBusinessObjectsControl.zip，取代原来的存根文件。</P>
<P>　　然后，系统就会把BEADocumentumControlProj.jar文件插入当前项目的库文件夹，这样项目就可以使用其中的功能了（请看下图）。</P>
<P>　　<IMG height=220 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102802.gif" width=231></P>
<P>　　Workshop还会把示例应用程序安装到BEAHOME\weblogic81\samples\partners\Documentum文件夹。最后，再把DBOC的帮助文件安装入Workshop的帮助系统，编排文件索引，使得可以在帮助的内容列表的扩展区域里使用DBOC的帮助。</P>
<P>　　有关扩展开发工具包的更多详细信息，可以在Workshop的帮助系统中找到，也可以在dev2dev.bea.com站点在线得到。</P>
<P><B>　　穿戴整齐，却无处可去</B><BR>　　完成这一步骤后，像大多数控件一样，会弹出了一个向导，让您开始填写信息。</P>
<P>　　但是， Documentum 环境要求我们要特别注意几个项目。和许多其他实用控件（报表生成器之类的）不同，DBOC控件不是自包含的实体，它实际是一个达到更大型的运行系统的桥梁。可以把它当作到数据库系统的连接。您不该试图把整个数据库系统拷贝到每个J2EE应用程序项目。相反，您应当指定共享的数据库服务器的位置，然后访问这些资源。在使用DBOC的情况下，我们需要指定Documentum基本类库(DFC)资源在开发人员机器上的位置。DFC只安装一份非常重要，这样DFC才不会重复，特别是要考虑到实际只需要Documentum业务对象注册表（DBOR）的一个实例，这样所有的业务对象都会包含在一个位置（否则会带来维护上的麻烦）。</P>
<P><B>　　关于DFC资源，需要了解两个WebLogic环境。</B> </P>
<UL>
<LI>需要更新Weblogic服务器域的SetDomainEnv.cmd 文件，以便在服务器的Classpath中可以找到DFC的jar文件。 
<LI>需要设置Workshop IDE 的应用程序属性，以便IDE识别DFC和BOF资源。这样， Workshop 可以做一些有用的工作，例如语法检测、自动完成等。 </LI></UL>　　DBOC的帮助系统给出了针对具体使用的Documentum版本的完整详细信息。认真遵守这些说明执行，包括重新启动Workshop环境后就应当准备就绪。 
<P><B>　　探索DBOC </B><BR>　　DBOC使得访问Documentum基于服务的对象非常简单。没有必要声明（或者根本不需要了解或考虑）home 和remote接口，只需要在向导中回答一些问题，就万事俱备了。 </P>
<P>　　<IMG height=344 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102803.gif" width=283></P>
<P>　　向导的头两步是所有Java控件都需要的公共步骤：</P>
<UL>
<LI>第一步是给控件代表的变量命名。 
<LI>在第二步，确定是利用现有控件的.jcx文件或者让向导为您建立一个新的.jcx文件。在上面的屏幕中，我们正在建立一个名为inbox.jcx的新控件。<BR>在第三步，要提供针对于DBOC的信息。 
<LI>所有的业务对象都在DBOR中注册。控件向导读取DBOR，并在下拉列表中列出所找到的全部SBO。<BR><BR><IMG height=155 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102804.gif" width=358><BR>上图只显示了默认的Documentum安装所提供的SBO。而在实际情况下，列表中会显示出所有从Documentum开发人员网站下载或者自行开发的SBO。 <BR>
<LI>最后，为DFCSessionManager（SessionManager）对象提供访问SBO所必需的凭证信息。我们会在本文后面详细介绍这方面内容。<BR>点击Create按钮后，向导就会建立一个指向新的（或现有的）控件文件的变量。在上面的示例中，向导建立了inbox.jcx文件，并把它放在当前项目中。 </LI></UL>
<P><B>　　控件文件里有什么？</B> <BR>　　DBOC不仅仅是SBO外面的简单包装器，它还加入了一些所有SBO都需要的实用特性。您的代码与代表SBO的控件进行交互。 </P>
<P>　　<IMG height=199 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102805.gif" width=448></P>
<P>　　在.jcx文件中的代码非常简单： </P>
<P>　　需要注意的关键一点是，控件文件本身只是扩展了SBO提供的Java接口以及DBOC自身提供的实用代码。向导对DBOR进行查询，然后建立新的接口，并把您提供的其他信息作为标签属性（docbase、 安全类型、用户名、口令） 保存在.jcx文件里。当在设计视图里查看控件实例时，控件的标签属性可以在属性编辑器中使用，可以在其中做任何必要的修改。</P>
<P>　　<IMG height=173 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102806.gif" width=279> </P>
<P>　　当WLW IDE 以图形方式显示控件时，它会枚举控件的所有可用方法，包括来自SBO的方法和来自控件的实用工具方法。</P>
<P>　　<IMG height=282 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102807.gif" width=317></P>
<P>　　凡是以getSBO… 和setSBO… 前缀开始的方法，都是DBOC自己提供的实用工具方法。可以用这些方法取得和设置控件的属性。</P>
<P>　　需要记住，用这些前缀开始的方法不与SBO对话，它们只是引用控件的属性。提供这些方法是为了在运行的时候可以使用控件自身的信息。有人可能会说，前缀应当是getSBOControl…和setSBOControl…，但是开发人员一般喜欢简洁，所以更短的名字保留了下来。所有剩下的方法则都是SBO自身提供的功能。</P>
<P><B>　　SessionManager对象</B><BR>　　SessionManager对象在DFC 5.1中引入，SessionManager对象是Documentum Business Object服务使用的所有会话所必需的接口。它负责集中实体、会话和事务。</P>
<P>　　DBOC设计用于减少需要手工编写的代码数量。所以，如果还不存在SessionManager对象，DBOC能够为您建立一个，这是合情合理的。</P>
<P>　　SessionManager对象负责表示试图访问SBO的Documentum用户的标识。即使SBO可能是不进行Docbase事务的Java代码，也是一样。</P>
<P>　　DBOC的安全类型属性接受三个值，分别代表处理用户凭证的三个不同方法： </P>
<UL>
<LI>Registered Identity<BR>这个值来自控件向导中选择“Use control generated Session Manager with the following identity”（使用控件生成的具有以下标识的Session Manager）。它提供了最简单的认证方法。SessionManager由控件本身（不是由客户端代码）生成。客户端只需把用户ID、口令和Docbase设置成控件的属性。SessionManager会由控件在运行时生成，根据控制中设置的凭证进行连接。 
<LI>Principal Identity<BR>这个值来自控件向导中选择“Use control generated SessionManager for principal identity”（使用控件生成的代表主体标识的SessionManager）。这个选项便于使用Documentum的Principal Security。与Registered Identities一样，SessionManager也是由控件本身自动生成的。<BR><BR>　　但是，必须使用控件客户端来创建并注册超级用户的凭证，并注册IdfPrincipalSupport对象。然后客户端只需把Principle User帐户设置为控件的属性。 <BR><BR>　　所以，您要提供能让SessionManager访问SBO的超级用户帐户名称和口令，还要提供没有口令的用户帐户名称（主体帐户）。SessionManager作用的时候，就好像主用户提供了正确的凭证一样。所有应用在用户帐户上的访问控制列表也都适用。<BR>　　对于超级用户执行管理性任务来说，这项技术特别有用，因为这些任务需要通过在运行时模拟用户来访问用户数据。<BR>
<LI>Assign SessionManager at Run-Time<BR><BR>　　当在控制向导中选择“Assign SessionManager at Run-Time”（在运行时分配SessionManager）时设置这个值。与前二个选项不同，这个选项要求控件客户端建立SessionManager对象，并把该对象作为控件的属性传入。它给控件客户端提供了最大的灵活性（当然在客户端代码需要的设置代码也最多）。在代码的其他地方已有SessionManager对象在使用时，这个选项最有用，有可能是在应用程序开始时设置一次SessionManager，而不用在控件初始化时设置。<BR><BR>页面流示例的示例代码演示了用于以上全部三种场景的技术，可以用作建立自己代码的指导。 </LI></UL>
<P><B>　　查看一些示例代码</B></P>
<P>　　下图显示了来自Web服务示例的代码片断。这个示例是个简单的演示，用DBOC访问用户的收件箱。它在收件箱项目之间循环，累计计数器，然后把收件箱中项目的数量返回给Web服务。</P>
<P>　　虽然这很基础，但是确实可以作为页面流示例的基础，可以采用这个技术，并在其上进行扩展，查询每个收件箱项目，然后把数据在Web页面上表示出来。</P>
<P>　　在写这篇文章的时候，关于收件箱SBO，几乎还没有公开的文档，所以这个代码恰好可以给您一个起步的地方。</P>
<P>　　为了让代码能够识别DFC API，需要在代码顶部加入import语句（一定记得服务器的Classpath已经更新，其中包含这些jar文件）。如果要访问另外的SBO，就必须更新Classpath，以便把它们也包含进来。</P>
<P>　　<IMG height=62 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102808.gif" width=382></P>
<P>　　可以看到，下图是一段混合代码，既处理控件本身，也处理DFC。</P>
<P>　　<IMG height=333 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102809.gif" width=380></P>
<P>　　在示例代码中，变量inbox是扩展SBO并具有DBOC实用工具方法的控件。</P>
<P>　　例如，第33行显示了一条语句如何能够根据控件的属性对SBO进行设置：inbox.setDocbase(inbox.getSBODocbase()); </P>
<P>　　这条语句告诉系统把SBO的Docbase属性设置成控件的docbase属性中所包含的值（在设计时设置的）。</P>
<P>　　下面，我们开始处理DFC的IInboxCollection和IInboxItem接口。</P>
<P>　　在第39行，代码通过getItem方法，请求控件把所有收件箱项目返回到一个集合。</P>
<P>　　要想查看inbox控件其他可以使用的方法，只要输入inbox和一个句点，然后让Workshop弹出方法列表（下图是部分列表）即可。</P>
<P>　　<IMG height=136 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102810.gif" width=411></P>
<P>　　代码剩下的部分就非常简单了；它对集合里的每个项目执行循环，增量计数器。循环完所有项目之后，方法就返回计数值。当然，这全都封闭在Try/Catch块里，以便捕获可能发生的任何错误。运行这个Web服务示例的结果就是一个简单的服务响应，在这个例子中，有三个收件箱项目被计数。</P>
<P>　　<IMG height=115 src="http://dev2dev.bea.com.cn/techdoc/wlworkshop/images/image2004102811.gif" width=258></P>
<P>　　一旦您知道了如何与收件箱SBO建立基本的连接，您就可以使用其他收件箱方法了。 </P>
<P>　　例如，如果您想修改页面流示例，让它按照发件人和日期对结果排序，您可以在用getItems方法填充集合之前，添加一个对setSortBy方法的调用。下面的代码根据发件人降序排列收件箱，在每个发件人里，则按发送日期升序排列。 </P>
<P>　　inbox.setSortBy("sent_by DESC, date_sent ASC", true );<BR>　　IInboxCollection iInboxCollection = inbox.getItems(); </P>
<P>　　setSortby方法接受二个参数：</P>
<UL>
<LI>第一个参数是一个字符串，定义OrderBy子句，可以简单到就是一个字段名，也可以复杂到包含多个字段和方向。 
<LI>第二个参数是一个布尔值，指明是否为更新的OrderBy子句（在这个例子里，必须为true）。 </LI></UL>
<P><BR><B>　　我们还要去哪里？</B><BR>　　本文对于Documentum业务对象控件做了更进一步的考察。</P>
<P>　　Business Object Framework是Documentum的战略组成部分，DBOC是BOF与J2EE框架集成的第一个实现。就在我们完成的时候，其他使用WebLogic Workshop框架的开发人员可以利用新的扩展开发工作包把他们自己的控件、服务和帮助提供并集成到Workshop IDE中。 </P>
<P>　　我们已经指出了一些DBOC提供的实用工具方法和安全性选项，利用它们可以在运行时使用SBO，从而实现更大的灵活性。最后，我们还显示了代码既需要处理控件本身，也需要处理SBO提供的方法。</P>
<P>　　DBOC允许您方便地把Documentum SBO集成到WebLogic Workshop环境。学习了这个集成，只需下载并安装WebLogic Platform，插入DBOC，修改一些WebLogic服务器的配置，并按照示例教程的指导即可。 </P>
<P>　　作者注：特别感谢Documentum的Kevin O'Connor，感谢他创建DBOC项目，支持Java控件工作，以及对本系列文章所做的贡献。</P>
<P>　　如果对本文有疑问，，请联系作者： Alan Zenreich ，电话：201-488-7222 转160.</P>
<P>　　<A href="http://dev2dev.bea.com.cn/techdoc/wlworkshop/2004102801.html">欢迎继续阅读本系列的第3部分 </A></P>
<P>　　原文出处：<BR>　　<A href="http://dev2dev.bea.com/products/wlworkshop81/articles/DCMT2.jsp" target=_blank>http://dev2dev.bea.com/products/wlworkshop81/articles/DCMT2.jsp</A></P><img src ="http://www.blogjava.net/kapok/aggbug/4068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-07 18:17 <a href="http://www.blogjava.net/kapok/archive/2005/05/07/4068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我自己的Java Control </title><link>http://www.blogjava.net/kapok/archive/2005/05/07/4060.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sat, 07 May 2005 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/07/4060.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4060.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/07/4060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4060.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4060.html</trackback:ping><description><![CDATA[<P><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17">http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17</A><BR><BR>作者：sdj21,rocksun(dev2dev ID)<BR><B>摘要：</B></P>
<P>　　本文介绍了如何使用JDK5.0的新特性Annotation和Spring框架来实现一种简单的JavaControl框架，主要实现了成员变量的自动带入，以及方法调用的权限检查控制，同时实现了一个JavaDatabaseControl的扩展，模仿workshop中的对应功能。<A id=0 name=0></A><BR><BR>目录：<BR><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">环境</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#2">背景资料</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#3">JavaControl最基本的功能--声明注入</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#4">使用动态代理来增加横切</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#5">Control方法的权限检查</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#6">一个DatabaseControl的特例</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#7">关于Demo程序</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#8">框架如何的加强 </A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#9">感受</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#11">代码下载</A><BR><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#10">参考文档</A><A id=1 name=1></A></P>
<P><B>环境</B> <A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#0">(目录)</A></P>
<P>由于使用了Annotation，所以必须准备好JDK5.0<A id=2 name=2></A></P>
<P><B>背景知识 </B><A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)</A></P>
<P><B>1</B><B>）Annotation</B><B>简介<BR></B>　　在Java领域，最早的Annotation就是JavaDoc了，将文档直接写在源程序里极大的方便了文档的编写，后来出现了许多有这种同步需求的工作，大家便发明了XDoclet，使用类似于JavaDoc的语法撰写描述信息，并使用工具生成描述文件。到JDK5.0出现以前这种需求已经更多了，许多工具和框架已经通过各种方式实现自己的这种标记，.NET更是率先推出了语言级的支持，所以JDK5.0终于推出了自己的Annotation。<BR>　　以下是两个简单的Annotation定义，定义的方式类似于接口，只是在interface前面多了个"@"<BR><BR>public @interface SampleAnnotation{<BR>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; String someValue;<BR>} <BR>public @interface NormalAnnotation{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String value1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int value2;<BR>}</P>
<P>　　然后我们在程序里可以这样使用Annotation，我们的编程工具或者是运行中的框架程序可以读取这些内容，生成代码或者是添加动作。</P>
<P>@SampleAnnotation (someValue ="Actual Value used in the program”)<BR>public void annotationUsingMethod(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; …<BR>} </P>
<P>　　Annotation主要是给工具开发商和框架开发者使用的工具，一般的编程人员可能仅仅是使用其他人开发的Annotation。Workshop在两年前已经开始尝试在开发工具里运用Annotaion，但当时没有语言级的支持，所有的Annotation都是以注释的形式出现，这样虽然灵活但是不严谨，也不适于推广，所以Annotation的出现可以大大方便开发商的工作，使得许多小开发商以及一般的架构设计人员也可以利用这种方式编程。<BR><I>注：本文中Annotation</I><I>可能对应的名字是“注释”或者是“说明”</I></P>
<P><B>2</B><B>）Spring</B><B>和DI</B><B>（IOC</B><B>）简介<BR>　　</B>在我看来，Spring和Annotation能完成很多相似的工作，它们之间的区别是在解决问题的位置并不相同。<BR>　　这是一个Spring的配置文件的信息，里面定义了三个bean，其中的exampleBean的属性中引用了其他的bean<BR><BR><BEAN class=examples.ExampleBean id=exampleBean><BR><property name="beanOne"><BR><REF bean="anotherExampleBean" /></property><BR><property name="beanTwo"><REF <br>bean="yetAnotherBean"/&gt;<BR></property><BR><property name="integerProperty">1</property><BR></BEAN><BR><BEAN class=examples.AnotherBean id=anotherExampleBean /><BR><BEAN class=examples.YetAnotherBean id=yetAnotherBean /><BR>　　我们用以下方式调用<BR><BR>InputStream is = new FileInputStream("beans.xml");<BR>XmlBeanFactory factory = new XmlBeanFactory(is);<BR>Object o = factory.get("exampleBean");<BR><BR>　　通过以上方式我们可以得到一个exampleBean的实例,并且里面的一些属性已经被预先注入了。<BR>　　在Spring结合了一些动态代理的以后，我们完全可以实现Annotation所能做到的许多功能。但是我们可以看出两种方式是不一样的，使用Spring面临着同步问题，维护配置文件比较的麻烦；而使用Annotation时，我们通常不容易在不改变原代码的时候改变一些特性，而且有时候也面临着代码复用的问题。本文并不讨论这些内容，本文里Spring只是一个bean的容器，用来存放JavaControl的注册信息，不会涉及依赖注入，而使用Annotation来完成相应的功能。<BR>　　<I>注：在本文里使用了两个配置文件，bean.xml</I><I>放置了我们测试用的Control</I><I>信息，在实际环境中可能需要不断添加新的Control</I><I>。另一个配置文件是config.xml</I><I>，里面是我们定义的ControlWrapper</I><I>，每当我们增加一种Control</I><I>的时候，如DatabaseControl</I><I>的时候，就需要添加一个Wrapper</I><I>。你应该首先看看这两个文件，里面有demo</I><I>程序的配置以及注册的Control</I><I>。<A id=3 name=3></A><BR><BR></I><B>JavaControl</B><B>最基本的功能--</B><B>声明注入<A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)</A><BR>　　</B>在JavaControl里编程的时候，我们通常并不会显式的初始化JavaControl，因为具体的实现不应该在程序里绑定，而应该完全的面向接口编程，我们只是简单的在声明Control的地方加一句简单的注释：</P>
<P>&nbsp; &nbsp;&nbsp;/**<BR>&nbsp;&nbsp;&nbsp; * @common:control<BR>&nbsp;&nbsp;&nbsp; */<BR>&nbsp;&nbsp; private myJavaControl.JCSecond jCSecond; &nbsp;&nbsp; </P>
<P>　　然后在我们的Control里就可以直接使用jCSecond,如下:&nbsp;<BR></P>
<P>&nbsp;&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp; * @common:operation<BR>&nbsp;&nbsp;&nbsp; */<BR>&nbsp;&nbsp; public String serviceA()<BR>&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("This is serviceA");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "This is serviceA"+":"+jCSecond.serviceB();<BR>&nbsp;&nbsp; }</P>
<P>　　Workshop&gt;平台会根据这些注释，自动生成代码，来完成初始化jCSecond的动作，来完成注入的工作。而我们的程序会在程序运行中读取注释信息，来完成注入工作，这与workshop不同。&nbsp;<BR>　　至于这个接口具体要使用哪个实现类,大家可以看看workshop的打包文件的META-INFjc-jar.xml,里面有所有JavaControl的说明，包括接口和实现，这说明了我们以后可以改变实现。<BR>　　下面我们实现自己的声明注入，这里我们需要一个JavaControlFactory类，作为进入Control环境的接口。两个Annotation，用来说明Control的实现类和包装器。JavaControlFactory类来读取Control中的注释信息，完成包装等工作。<BR><BR><B>1）首先我们需要定义一个修饰成员变量的Annotation，所有被当作Control的成员变量都可以使用这个annotation来说明，完整的定义如下：<BR><BR></B>package net.rocksun.tiffany.annotation;<BR>import java.lang.annotation.*;</P>
<P>@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)</P>
<P>@Target({java.lang.annotation.ElementType.FIELD})</P>
<P>public @interface Control {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String name();<BR>}<BR></P>
<P>　　这是个简单的annotation，@Retention说明本Annotation要保留到什么时候，因为我们需要在程序运行时动态的读取，所以设置为RUNTIME。@Target说明了本annotation所针对的对象，是FIELD。</P>
<P>　　我们在Control里这样使用这个annotation：</P>
<P>&nbsp;&nbsp;&nbsp; @Control(name="second")<BR>&nbsp;&nbsp; private SecondControl second;&nbsp;&nbsp; <BR><BR>　　name是SecondControl的实现类名称，我们在这里说明，然后通过JavaControlFactory来根据名字自动的将SecondControl实例化。</P>
<P><B>2</B><B>）我们还需要一个说明类的ControlType</B><B>，用来注释所有的Control</B><B>类： </B></P>
<P>package net.rocksun.tiffany.annotation;</P>
<P>import java.lang.annotation.*;</P>
<P>@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)<BR>@Target({java.lang.annotation.ElementType.TYPE})<BR>public @interface ControlType {<BR>&nbsp;&nbsp; public String name();<BR>}</P>
<P>　　它也是运行级别保留，但它针对的对象是类，只有类可以使用这个annotation注释。name表示这个类需要的包装器，也就是用来对这个类进行特殊处理的类。<BR>　　使用方式如下 </P>
<P>.............<BR>@ControlType(name="DefaultControl")<BR>public class FirstControlImpl implements FirstControl {<BR>.............<BR><BR>@ControlType(name="DefaultControl")说明这个类需要使用DefaultControl类型的包装器包装一下。</P>
<P><B>3</B><B>）ControlFactory</B><B>实现<BR>　　</B>我们希望从JavaControl中取得类的方式为FirstControl first = (FirstControl)factory.getControl("first");并且first中所有的标记为Control的成员变量都可以被正确的初始化。<BR>　　所以我们的ControlFactory首先应该遍历类first的所有成员变量，当遇到使用Control注释的成员变量,根据它的类型进行实例化。以下是遍历所有成员变量的过程：<BR></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; fields.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //判断是否为Control，也就是检查是否使用@Control注释<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(isControl(fields[i])){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object tempBean = getControlInstance(fields[i]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(tempBean==null){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IllegalArgumentException(bean.getClass()+":"+fields[i].getName()+"’s annotation error");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果检查配置没有问题，就设置这个值，同时检查这个Bean的成员是不是也需要实例化<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wrappedBean.setPropertyValue(fields[i].getName(), tempBean);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initBean(tempBean);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR><BR>　　需要注意的是，所有的Control应该使用JavaBean的方式，所有成员变量应该有无参的构造方法，并且成员都有get和set方法。<BR>　　其中isControl方法如下： </P>
<P>&nbsp;&nbsp;&nbsp; private boolean isControl(Field field){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Annotation[] annotation = field.getDeclaredAnnotations();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; annotation.length; i++){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(annotation[i] instanceof Control){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<BR>&nbsp;&nbsp; }</P>
<P>　　我们遍历这个字段的所有annotation，来检查有没有Control，如果有就执行getControlInstance，来实例化这个Control。getControlInstance会检查我们在类上作的ControlType注释，来选择包装器，进行bean的包装。&nbsp;&nbsp;&nbsp; </P>
<P><B>4</B><B>）在实例化完成后，根据类的ControlType Annotation</B><B>我们可以对类进行包装，每一个包装类实现如下接口： </B></P>
<P>public interface ControlWrapper {<BR>&nbsp;&nbsp; public Object wrapBean(Object bean);<BR>}<BR></P>
<P>　　这样根据指定的ControlType的不同，我们还可以另外使用我们自定义的Control包装器，进行特殊的操作，我们首先实现了一个默认的DefaultControlType，不作任何操作。<BR>　　到目前，我们的JavaControl最基本框架已经完成了，我们可以测试结果，可以看到，我们通过注释说明，就可以实现对成员变量的自动初始化，我们通过外部文件的配置就可以在以后方便的修改实现类。<BR>　　可以察看源代码中ControlFactoryTest的testGetControl的运行结果。<A id=4 name=4></A><BR><BR><B>使用动态代理来增加横切<A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)</A><BR>　　</B>对于每一个方法被执行的时候，我们希望可以进行一种横切，如每一个方法执行前我们要记一个Log，这就需要添加一个动态代理，当然我们也可以使用其他种方式，但是动态代理是最优雅的。<BR>　　我们增加DefaultControlProxy类来处理横切的动作，DefaultControlProxy是InvocationHandler的子类，实现了ivoke方法，代码如下： </P>
<P>&nbsp;&nbsp;&nbsp; public Object invoke(Object proxy, Method method, Object[] args) throws<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.info("method "+method.getName()+" invoked");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return method.invoke(getDelegate(),args);<BR>&nbsp;&nbsp; }&nbsp;&nbsp; </P>
<P>　　我们不作额外的操作，只是log被执行的方法，这样，所有的Control方法在执行的时候都会纪录log，这里的delegate是我们原来的对象，我们保留这个还有很多用处。　　为了完成这些横切，我们必须修改DefaultControlWrapper，加入代码如下</P>
<P>public Object wrapBean(Object bean) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultControlProxy proxy = new DefaultControlProxy();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy.setDelegate(bean);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("wrap................"+bean.getClass());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Proxy.newProxyInstance(this.getClass().getClassLoader(),bean.getClass().getInterfaces(),proxy);<BR>}</P>
<P>　　重新运行我们的测试，结果并不影响。只是在每个Control的方法执行以前，打印了句Log信息。<A id=5 name=5></A><BR><BR><B>Control</B><B>方法的权限检查<A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)</A><BR>　　</B>workshop可以指定一个方法的角色，我们已经有了横切，所以这步工作也并不困难。首先定义一个Annotation，名字是Role，代码如下：</P>
<P>@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)<BR>@Target( {java.lang.annotation.ElementType.METHOD})<BR>public @interface Role {<BR>&nbsp;&nbsp; public String name();<BR>}</P>
<P>　　没有好说的，与前面及唯一的区别就是对象变成了方法，我们可以这样声明来表示这个方法必需要角色admin来参与：</P>
<P>&nbsp;&nbsp;&nbsp; @Role(name="admin")<BR>&nbsp;&nbsp; public String doTaskWithRole(){<BR>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return "admin";<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>　　对于每一个Control必须在建立的时候，告诉他所拥有的角色，所以修改所有JavaControl的基类BaseControl如下： </P>
<P>&nbsp;&nbsp;&nbsp; private String role;<BR>&nbsp;&nbsp; public BaseControl() {<BR>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; public void setRole(String role) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.role = role;<BR>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; public String getRole() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return role;<BR>&nbsp;&nbsp; }<BR><BR>　　有了这些修改，我们可以在我们的横切代码那里增加检查权限的功能</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method m = delegate.getClass().getMethod(method.getName(),classes);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Annotation[] annotation = m.getAnnotations();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; annotation.length; i++) {<BR>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(annotation[i] instanceof Role){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.info("Method "+method.getName()+" should check role");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(((BaseControl)getDelegate()).getRole().equals(((Role)annotation[i]).name())){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;throw&nbsp; new IllegalAccessException("Method "+method.getName()+" requier Role "+((Role)annotation[i]).name());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>　　以上代码就是增加的检查，我们使用Method m = delegate.getClass().getMethod(method.getName())得到新的Method，因为传递给我们的Method对象是代理过的，所以我们必须使用原始的类的定义，当出现权限不足时，一个例外就会抛出。对于我们的测试用例，我们增加testHasRoleCheckFailed()&gt;和testHasRoleCheckSuccess()&gt;用例，分别测试在没有和有权限的时候会不会有例外发生。<A id=6 name=6></A></P>
<P><B>一个DatabaseControl</B><B>的特例 <A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)<BR></A></B>　　我们可以扩展我们的JavaControl了,我们定义一个新的Annotation----DatabaseMethod</P>
<P>@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)<BR>@Target({java.lang.annotation.ElementType.METHOD})<BR>public @interface DatabaseMethod {<BR>&nbsp;&nbsp; public String sql();<BR>&nbsp;&nbsp; public String dataSource() default "dataSource";<BR>}</P>
<P>　　这个Annotation有两个成员，其中dataSource()有缺省值，也就是程序里注释的时候，可以不输入dataSource参数。 <BR></P>
<P>@ControlType(name="DatabaseControl")<BR>public class DatabaseControl1Impl extends BaseControl implements DatabaseControl1{<BR>&nbsp;&nbsp; @DatabaseMethod(sql="insert into user values(:0,:1)")<BR>&nbsp;&nbsp; public void insertUser(int id , String name) {<BR>&nbsp;&nbsp; }<BR>} <BR></P>
<P>　　这就是一个使用DatabaseControl的例子，首先说明这个Control是一个DatabaseControl，需要使用net.rocksun.tiffany.wrapper.DatabaseControlWrapper来进行包装。再就是对应的我们要修改DatabaseControlWrapper类，同时参照DefaultControl，我们要对DatabaseControlProxy进行修改，使之可以处理sql:</P>
<P>public Object invoke(Object proxy, Method method, Object[] args) throws<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Throwable {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.info(delegate.getClass()+ "’s method "+method.toString()+" invoked");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class[] classes = null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(args!=null){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classes = new Class[args.length];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; args.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classes[i] = args[i].getClass();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(args[i] instanceof Integer){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classes[i] = Integer.TYPE;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method m = delegate.getClass().getMethod(method.getName(),classes);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Annotation[] annotation = m.getAnnotations();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; annotation.length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(annotation[i] instanceof DatabaseMethod){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String sql = ((DatabaseMethod)annotation[i]).sql();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String dataSource = ((DatabaseMethod)annotation[i]).dataSource();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; args.length; j++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;if(args[j] instanceof Integer){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sql = sql.replace(":"+j,args[j].toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sql = sql.replace(":"+j,"’"+args[j].toString()+"’");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.info(" will execute sql ’"+ sql +"’ for dataSource ’"+dataSource+"’");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return method.invoke(getDelegate(),args);<BR>&nbsp;&nbsp; }<BR><BR>　　这是整个方法的定义，并没有考虑到所有的类型，也没有实际执行sql，只是告诉大家我们这个时候已经有了操作数据库的能力了。运行测试用例的testInsertUser，我们就可以看到我们把sql处理成可以执行的状态。<A id=7 name=7></A><BR><BR><B>关于Demo</B><B>程序<BR>　　</B>所有的Demo Control都在net.rocksun.tiffany.demo下，里面有FirstControl,SecondControl,DatabaseControl1三个Control和它们的实现，所有的Control实现都是BaseControl的子类，BaseControl帮助它的子类保存角色信息。<BR>　　FirstControl中有SecondControl和DatabaseControl1的一个实例，FirstControl的三个方法，分别为doTask，doTaskWithRole，insertUser，其中第一个演示了成员变量的自动注入，第二个包括了权限检查，第三个是调用DatabaseControl控件。<BR>　　有了这些，我们就可以使用ControlFactoryTest进行测试<A id=8 name=8></A></P>
<P><B>框架如何的加强<A href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=17#1">(目录)<BR></A></B>　　作为框架程序，还有许多事情要做。如数据库控件，我们可以添加一种事物控制标示，在需要启动事物的方法前添加一种annotation，方法里所有DatabaseControl使用相同的数据库连接，并且根据抛出的Exception来选择提交和会滚。有时候建立一种通用的控件框架是比较麻烦的，但如果在一个自己写的框架下，在方法前增加注释来说明一些额外的操作也是很方便的选择。<BR>　　由于没有工具，所以使用我们自己的Control框架并不容易，需要自己写接口，然后是实现，然后是配置信息，而使用workshop则可以只关心写实现，接口和配置都交给workshop自动的完成。使用Control最方便的地方就是可以很好的与编程工具结合，并且可以使代码更规范，但如果没有工具，就比较难说了。<BR>　　目前这个框架还有很多问题，注册新控件太麻烦，包装程序重复工作太多，还没有很好的复用。再就是BaseControl应该更好的包装，空间声明应该使用新Annotation类型，而不应该使用名称作为参数的方式，因为使用新类型，可以在编译时检查，减少错误发生的可能。<BR>　　对于页面流等技术我们已可以自己实现，但核心思想是一样的。 </P>
<P><B>感受<BR>　　</B>记得看《XDoclet in Action》的时候，说XDoclet生成配置文件的时候，有两种目的，一种是直接可以用，另一种是作为模版，第一种方式更好一些，第二种通常是没有办法时的选择。Annotation和Spring其实就是XDoclet的两种状态，当Annotation是代码级的时候，我们可以用工具得到对应的Spring配置文件，但是这就意味着，我们可能最好不要直接修改Spring的配置文件，因为下一次生成就把这些修改覆盖了，但这样就失去了通过配置来灵活改变一些设置的可能性。同样，如果我们完全依赖自己配置文件，可能会很麻烦。<BR>　　但我觉得使用配置虽然麻烦一些，但是确实是更清楚一些，如果有可能的话应该结合使用，实现灵活性与简单性的结合。<BR>　　Annotation，DIIOC）等等，这些东西出现的目的都差不多，都是为了减少这些横切代码的重复工作。我也越来越感觉万变不离其宗了。JavaControl就是想提供这样一个环境，在这个环境里可以自动的读取Annotation来完成横切的工作，这比使用spring确实方便一些，因为没有这样一个环境，Spring只能通过配置实现，这样的重复工作也太多。 <A id=11 name=11></A><BR><BR>　　<B><A href="http://dev2dev.bea.com.cn/images/tiffany.rar" target=_blank>本文代码下载</A> </B>(请选择目标另存为 tiffany.rar)</P>
<P><B>参考文档<BR>　　</B>这一次几乎没参考什么，只是看了些Annotation的参考，但是忘记了地址。<BR>　　如果有就是这个了：oreilly_.java.1.5.tiger.a.developers.notebook.(2004)</P>
<P><B>关于作者：<BR>　　</B>sdj21,rocksun(dev2dev ID)，软件工程师，<BR>　　邮件地址：<A href="mailto:sdj21@sina.com">sdj21@sina.com</A> , <A href="mailto:daijun@gmail.com">daijun@gmail.com</A> ,<A href="mailto:sundaijun@126.com">sundaijun@126.com</A></P><img src ="http://www.blogjava.net/kapok/aggbug/4060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-07 14:30 <a href="http://www.blogjava.net/kapok/archive/2005/05/07/4060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PhpLDAPAdmin</title><link>http://www.blogjava.net/kapok/archive/2005/05/05/4042.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 05 May 2005 11:00:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/05/4042.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4042.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/05/4042.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4042.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4042.html</trackback:ping><description><![CDATA[<A href="http://phpldapadmin.sourceforge.net/">http://phpldapadmin.sourceforge.net/</A><img src ="http://www.blogjava.net/kapok/aggbug/4042.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-05 19:00 <a href="http://www.blogjava.net/kapok/archive/2005/05/05/4042.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EJB的部署艺术</title><link>http://www.blogjava.net/kapok/archive/2005/05/05/4041.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 05 May 2005 10:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/05/4041.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4041.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/05/4041.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4041.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4041.html</trackback:ping><description><![CDATA[<A href="http://www0.ccidnet.com/tech/app/2001/09/07/58_3215.html">http://www0.ccidnet.com/tech/app/2001/09/07/58_3215.html</A><img src ="http://www.blogjava.net/kapok/aggbug/4041.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-05 18:40 <a href="http://www.blogjava.net/kapok/archive/2005/05/05/4041.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是LDAP?</title><link>http://www.blogjava.net/kapok/archive/2005/05/05/4036.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 05 May 2005 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/05/4036.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4036.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/05/4036.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4036.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4036.html</trackback:ping><description><![CDATA[<A href="http://www.trainlinux.com/n/2002-05-05/5025.html">http://www.trainlinux.com/n/2002-05-05/5025.html</A><BR><BR>LDAP的英文全称是Lightweight Directory Access Protocol，一般都简称为LDAP。它是基于X.500标准的， <BR>但是简单多了并且可以根据需要定制。与X.500不同，LDAP支持TCP/IP，这对访问Internet是必须的。LDAP <BR>的核心规范在RFC中都有定义，所有与LDAP相关的RFC都可以在LDAPman RFC网页中找到。现在LDAP技术不仅 <BR>发展得很快而且也是激动人心的。在企业范围内实现LDAP可以让运行在几乎所有计算机平台上的所有的应 <BR>用程序从LDAP目录中获取信息。LDAP目录中可以存储各种类型的数据：电子邮件地址、邮件路由信息、人力 <BR>资源数据、公用密匙、联系人列表，等等。通过把LDAP目录作为系统集成中的一个重要环节，可以简化员工 <BR>在企业内部查询信息的步骤，甚至连主要的数据源都可以放在任何地方。 <BR><BR>LDAP目录的优势 <BR><BR>如果需要开发一种提供公共信息查询的系统一般的设计方法可能是采用基于WEB的数据库设计方式，即前端 <BR>使用浏览器而后端使用WEB服务器加上关系数据库。后端在Windows的典型实现可能是Windows NT + IIS + Acess <BR>数据库或者是SQL服务器，IIS和数据库之间通过ASP技术使用ODBC进行连接，达到通过填写表单查询数据的功能； <BR><BR>后端在Linux系统的典型实现可能是Linux+ Apache + postgresql，Apache和数据库之间通过PHP3提供的函数进 <BR>行连接。使用上述方法的缺点是后端关系数据库的引入导致系统整体的性能降低和系统的管理比较繁琐，因为需 <BR>要不断的进行数据类型的验证和事务的完整性的确认；并且前端用户对数据的控制不够灵活，用户权限的设置一 <BR>般只能是设置在表一级而不是设置在记录一级。 <BR><BR>目录服务的推出主要是解决上述数据库中存在的问题。目录与关系数据库相似，是指具有描述性的基于属性的记 <BR>录集合，但它的数据类型主要是字符型，为了检索的需要添加了BIN（二进制数据）、CIS（忽略大小写）、CES <BR>（大小写敏感）、TEL（电话型）等语法（Syntax），而不是关系数据库提供的整数、浮点数、日期、货币等类型， <BR>同样也不提供象关系数据库中普遍包含的大量的函数，它主要面向数据的查询服务（查询和修改操作比一般是大于 <BR>10:1），不提供事务的回滚（rollback）机制，它的数据修改使用简单的锁定机制实现All-or-Nothing，它的目标 <BR>是快速响应和大容量查询并且提供多目录服务器的信息复制功能。 <BR><BR>现在该说说LDAP目录到底有些什么优势了。现在LDAP的流行是很多因数共同作用的结果。可能LDAP最大的优势是： <BR>可以在任何计算机平台上，用很容易获得的而且数目不断增加的LDAP的客户端程序访问LDAP目录。而且也很容易 <BR>定制应用程序为它加上LDAP的支持。 <BR><BR>LDAP协议是跨平台的和标准的协议，因此应用程序就不用为LDAP目录放在什么样的服务器上操心了。实际上，LDAP <BR>得到了业界的广泛认可，因为它是Internet的标准。产商都很愿意在产品中加入对LDAP的支持，因为他们根本不用 <BR>考虑另一端（客户端或服务端）是怎么样的。LDAP服务器可以是任何一个开发源代码或商用的LDAP目录服务器（或 <BR>者还可能是具有LDAP界面的关系型数据库），因为可以用同样的协议、客户端连接软件包和查询命令与LDAP服务器 <BR>进行交互。与LDAP不同的是，如果软件产商想在软件产品中集成对DBMS的支持，那么通常都要对每一个数据库服务 <BR>器单独定制。不象很多商用的关系型数据库，你不必为LDAP的每一个客户端连接或许可协议付费 大多数的LDAP服务 <BR>器安装起来很简单，也容易维护和优化。 <BR><BR>LDAP服务器可以用“推”或“拉”的方法复制部分或全部数据，例如：可以把数据“推”到远程的办公室，以增加 <BR>数据的安全性。复制技术是内置在LDAP服务器中的而且很容易配置。如果要在DBMS中使用相同的复制功能，数据库 <BR>产商就会要你支付额外的费用，而且也很难管理。 <BR><BR>LDAP允许你根据需要使用ACI（一般都称为ACL或者访问控制列表）控制对数据读和写的权限。例如，设备管理员可 <BR>以有权改变员工的工作地点和办公室号码，但是不允许改变记录中其它的域。ACI可以根据谁访问数据、访问什么数 <BR>据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由LDAP目录服务器完成的，所以不用担心在客 <BR>户端的应用程序上是否要进行安全检查。 <BR><BR>LDAP（Lightweight Directory Acess Protocol）是目录服务在TCP/IP上的实现（RFC 1777 V2版和RFC 2251 <BR><BR>V3版）。它是对X500的目录协议的移植，但是简化了实现方法，所以称为轻量级的目录服务。在LDAP中目录是按照 <BR>树型结构组织，目录由条目（Entry）组成，条目相当于关系数据库中表的记录；条目是具有区别名DN（Distinguished <BR><BR>Name）的属性（Attribute）集合，DN相当于关系数据库表中的关键字（Primary <BR><BR>Key）；属性由类型（Type）和多个值（Values）组成，相当于关系数据库中的域（Field）由域名和数据类型组成， <BR>只是为了方便检索的需要，LDAP中的Type可以有多个Value，而不是关系数据库中为降低数据的冗余性要求实现的各 <BR>个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织，非常的直观。LDAP把数据存放在 <BR>文件中，为提高效率可以使用基于索引的文件数据库，而不是关系数据库。LDAP协议集还规定了DN的命名方法、存 <BR>取控制方法、搜索格式、复制方法、URL格式、开发接口等 <BR><BR>LDAP对于这样存储这样的信息最为有用，也就是数据需要从不同的地点读取，但是不需要经常更新。 <BR><BR>例如，这些信息存储在LDAP目录中是十分有效的： <BR><BR>l 公司员工的电话号码簿和组织结构图 <BR><BR>l 客户的联系信息 <BR><BR>l 计算机管理需要的信息，包括NIS映射、email假名，等等 <BR><BR>l 软件包的配置信息 <BR><BR>l 公用证书和安全密匙 <BR><BR>什么时候该用LDAP存储数据 <BR><BR>大多数的LDAP服务器都为读密集型的操作进行专门的优化。因此，当从LDAP服务器中读取数据的时候会比从专门为 <BR>OLTP优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化，大多数的LDAP目录服务器 <BR>并不适合存储需要需要经常改变的数据。例如，用LDAP服务器来存储电话号码是一个很好的选择，但是它不能作为 <BR>电子商务站点的数据库服务器。 <BR><BR>如果下面每一个问题的答案都是“是”，那么把数据存在LDAP中就是一个好主意。 <BR><BR>l 需要在任何平台上都能读取数据吗？ <BR><BR>l 每一个单独的记录项是不是每一天都只有很少的改变？ <BR><BR>l 可以把数据存在平面数据库（flat database）而不是关系型数据库中吗？换句话来说，也就是不管什么范式不 <BR>范式的，把所有东西都存在一个记录中（差不多只要满足第一范式）。 <BR><BR>最后一个问题可能会唬住一些人，其实用平面数据库去存储一些关系型的数据也是很一般的。例如，一条公司员工 <BR>的记录就可以包含经理的登录名。用LDAP来存储这类信息是很方便的。一个简单的判断方法：如果可以把保数据存 <BR>在一张张的卡片里，就可以很容易地把它存在LDAP目录里。 <BR><BR><BR>安全和访问控制 <BR><BR>LDAP提供很复杂的不同层次的访问控制或者ACI。因这些访问可以在服务器端控制，这比用客户端的软件保证数据的 <BR>安全可安全多了。 <BR><BR>用LDAP的ACI，可以完成： <BR><BR>l 给予用户改变他们自己的电话号码和家庭地址的权限，但是限制他们对其它数据（如，职务名称，经理的登录名， <BR>等等）只有“只读”权限。 <BR><BR>l 给予“HR-admins"组中的所有人权限以改变下面这些用户的信息：经理、工作名称、员工号、部门名称和部门号。 <BR>但是对其它域没有写权限。 <BR><BR>l 禁止任何人查询LDAP服务器上的用户口令，但是可以允许用户改变他或她自己的口令。 <BR><BR>l 给予经理访问他们上级的家庭电话的只读权限，但是禁止其他人有这个权限。 <BR><BR>l 给予“host-admins"组中的任何人创建、删除和编辑所有保存在LDAP服务器中的与计算机主机有关的信息 <BR><BR>l 通过Web，允许“foobar-sales"组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这 <BR>将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理（PDA）上。（如果销售人员的软件都支持LDAP， <BR>这将非常有用） <BR><BR>l 通过Web，允许组的所有者删除或添加他们拥有的组的成员。例如：可以允许销售经理给予或禁止销售人员改变Web <BR>页的权限。也可以允许邮件假名（mail aliase）的所有者不经过IT技术人员就直接从邮件假名中删除或添加用户。 <BR>“公用”的邮件列表应该允许用户从邮件假名中添加或删除自己（但是只能是自己）。也可以对IP地址或主机名加以 <BR>限制。例如，某些域只允许用户IP地址以192.168.200.*开头的有读的权限，或者用户反向查找DNS得到的主机名必须 <BR>为*.foobar.com。 <BR><BR>LDAP目录树的结构 <BR><BR>LDAP目录以树状的层次结构来存储数据。如果你对自顶向下的DNS树或UNIX文件的目录树比较熟悉，也就很容易掌握 <BR>LDAP目录树这个概念了。就象DNS的主机名那样，LDAP目录记录的标识名（Distinguished Name，简称DN）是用来读取 <BR>单个记录，以及回溯到树的顶部。后面会做详细地介绍。 <BR><BR>为什么要用层次结构来组织数据呢？原因是多方面的。下面是可能遇到的一些情况： <BR><BR>l 如果你想把所有的美国客户的联系信息都“推”到位于到西雅图办公室（负责营销）的LDAP服务器上，但是你不想 <BR>把公司的资产管理信息“推”到那里。 <BR><BR>l 你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里，资产管理组对“asset-mgmt"部分有完 <BR>全的访问权限，但是不能访问其它地方。 <BR><BR>l 把LDAP存储和复制功能结合起来，可以定制目录树的结构以降低对WAN带宽的要求。位于西雅图的营销办公室需要每 <BR>分钟更新的美国销售状况的信息，但是欧洲的销售情况就只要每小时更新一次就行了。 <BR><BR>刨根问底：基准DN <BR><BR>LDAP目录树的最顶部就是根，也就是所谓的“基准DN"。基准DN通常使用下面列出的三种格式之一。假定我在名为FooBar <BR>的电子商务公司工作，这家公司在Internet上的名字是foobar.com。 <BR><BR>o="FooBar, Inc.", c=US <BR><BR>（以X.500格式表示的基准DN） <BR><BR>在这个例子中，o=FooBar, Inc. 表示组织名，在这里就是公司名的同义词。c=US 表示公司的总部在美国。以前，一般 <BR>都用这种方式来表示基准DN。但是事物总是在不断变化的，现在所有的公司都已经（或计划）上Internet上。随着 <BR>Internet的全球化，在基准DN中使用国家代码很容易让人产生混淆。现在，X.500格式发展成下面列出的两种格式。 <BR><BR>o=foobar.com <BR><BR>（用公司的Internet地址表示的基准DN） <BR><BR>这种格式很直观，用公司的域名作为基准DN。这也是现在最常用的格式。 <BR><BR>dc=foobar, dc=com <BR><BR>（用DNS域名的不同部分组成的基准DN） <BR><BR>就象上面那一种格式，这种格式也是以DNS域名为基础的，但是上面那种格式不改变域名（也就更易读），而这种格式 <BR>把域名：foobar.com分成两部分 dc=foobar, dc=com。在理论上，这种格式可能会更灵活一点，但是对于最终用户来说 <BR>也更难记忆一点。考虑一下foobar.com这个例子。当foobar.com和gizmo.com合并之后，可以简单的把“dc=com"当作基 <BR>准DN。把新的记录放到已经存在的dc=gizmo, dc=com目录下，这样就简化了很多工作（当然，如果foobar.com和wocket.edu <BR>合并，这个方法就不能用了）。如果LDAP服务器是新安装的，我建议你使用这种格式。再请注意一下，如果你打算使用活动 <BR>目录（Actrive Directory），Microsoft已经限制你必须使用这种格式。 <BR><BR>更上一层楼：在目录树中怎么组织数据 <BR><BR>在UNIX文件系统中，最顶层是根目录（root）。在根目录的下面有很多的文件和目录。象上面介绍的那样，LDAP目录也是 <BR>用同样的方法组织起来的。 <BR><BR>在根目录下，要把数据从逻辑上区分开。因为历史上（X.500）的原因，大多数LDAP目录用OU从逻辑上把数据分开来。OU <BR>表示“Organization Unit"，在X.500协议中是用来表示公司内部的机构：销售部、财务部，等等。现在LDAP还保留ou=这 <BR>样的命名规则，但是扩展了分类的范围，可以分类为：ou=people, ou=groups, ou=devices，等等。更低一级的OU有时用 <BR>来做更细的归类。例如：LDAP目录树（不包括单独的记录）可能会是这样的： <BR><BR>dc=foobar, dc=com <BR><BR>ou=customers <BR><BR>ou=asia <BR><BR>ou=europe <BR><BR>ou=usa <BR><BR>ou=employees <BR><BR>ou=rooms <BR><BR>ou=groups <BR><BR>ou=assets-mgmt <BR><BR>ou=nisgroups <BR><BR>ou=recipes <BR><BR>单独的LDAP记录 <BR><BR>DN是LDAP记录项的名字 <BR><BR>在LDAP目录中的所有记录项都有一个唯一的“Distinguished Name"，也就是DN。每一个LDAP记录项的DN是由两个部分 <BR>组成的：相对DN（RDN）和记录在LDAP目录中的位置。 <BR><BR>RDN是DN中与目录树的结构无关的部分。在LDAP目录中存储的记录项都要有一个名字，这个名字通常存在cn（Common Name） <BR>这个属性里。因为几乎所有的东西都有一个名字，在LDAP中存储的对象都用它们的cn值作为RDN的基础。如果我把最喜欢的 <BR>吃燕麦粥食谱存为一个记录，我就会用cn=Oatmeal Deluxe作为记录项的RDN。 <BR><BR>l 我的LDAP目录的基准DN是dc=foobar,dc=com <BR><BR>l 我把自己的食谱作为LDAP的记录项存在ou=recipes <BR><BR>l 我的LDAP记录项的RDN设为cn=Oatmeal Deluxe <BR><BR>上面这些构成了燕麦粥食谱的LDAP记录的完整DN。记住，DN的读法和DNS主机名类似。下面就是完整的DN： <BR><BR>cn=Oatmeal Deluxe,ou=recipes,dc=foobar,dc=com <BR><BR>举一个实际的例子来说明DN <BR><BR>现在为公司的员工设置一个DN。可以用基于cn或uid（User ID），作为典型的用户帐号。例如，FooBar的员工Fran Smith <BR>（登录名：fsmith）的DN可以为下面两种格式： <BR><BR>uid=fsmith,ou=employees,dc=foobar,dc=com <BR><BR>（基于登录名） <BR><BR>LDAP（以及X.500）用uid表示“User ID"，不要把它和UNIX的uid号混淆了。大多数公司都会给每一个员工唯一的登录名， <BR>因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫Fran Smith的加入公司，如果Fran改变了她的 <BR>名字（结婚？离婚？或宗教原因？），也用不着改变LDAP记录项的DN。 <BR><BR>cn=Fran Smith,ou=employees,dc=foobar,dc=com <BR><BR>（基于姓名） <BR><BR>可以看到这种格式使用了Common Name（CN）。可以把Common Name当成一个人的全名。这种格式有一个很明显的缺点就是： <BR>如果名字改变了，LDAP的记录就要从一个DN转移到另一个DN。但是，我们应该尽可能地避免改变一个记录项的DN。 <BR><BR>定制目录的对象类型 <BR><BR>你可以用LDAP存储各种类型的数据对象，只要这些对象可以用属性来表示，下面这些是可以在LDAP中存储的一些信息： <BR><BR>l 员工信息：员工的姓名、登录名、口令、员工号、他的经理的登录名，邮件服务器，等等。 <BR><BR>l 物品跟踪信息：计算机名、IP地址、标签、型号、所在位置，等等。 <BR><BR>l 客户联系列表：客户的公司名、主要联系人的电话、传真和电子邮件，等等。 <BR><BR>l 会议厅信息：会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。 <BR><BR>l 食谱信息：菜的名字、配料、烹调方法以及准备方法。 <BR><BR>因为LDAP目录可以定制成存储任何文本或二进制数据，到底存什么要由你自己决定。LDAP目录用对象类型 <BR>（object classes）的概念来定义运行哪一类的对象使用什么属性。在几乎所有的LDAP服务器中，你都要根据 <BR>自己的需要扩展基本的LDAP目录 <BR>的功能，创建新的对象类型或者扩展现存的对象类型。 <BR><BR>LDAP目录以一系列“属性对”的形式来存储记录项，每一个记录项包括属性类型和属性值（这与关系型数据库 <BR>用行和列来存取数据有根本的不同）。下面是我存在LDAP目录中的一部分食谱记录： <BR><BR>dn: cn=Oatmeal Deluxe, ou=recipes, dc=foobar, dc=com <BR><BR>cn: Instant Oatmeal Deluxe <BR><BR>recipeCuisine: breakfast <BR><BR>recipeIngredient: 1 packet instant oatmeal <BR><BR>recipeIngredient: 1 cup water <BR><BR>recipeIngredient: 1 pinch salt <BR><BR>recipeIngredient: 1 tsp brown sugar <BR><BR>recipeIngredient: 1/4 apple, any type <BR><BR>请注意上面每一种配料都作为属性recipeIngredient值。LDAP目录被设计成象上面那样为一个属性保存多个值的， <BR>而不是在每一个属性的后面用逗号把一系列值分开。 <BR><BR>因为用这样的方式存储数据，所以数据库就有很大的灵活性，不必为加入一些新的数据就重新创建表和索引。更 <BR>重要的是，LDAP目录不必花费内存或硬盘空间处理“空”域，也就是说，实际上不使用可选择的域也不会花费你 <BR>任何资源。 <BR><BR>作为例子的一个单独的数据项 <BR><BR>让我们看看下面这个例子。我们用Foobar, Inc.的员工Fran Smith的LDAP记录。这个记录项的格式是LDIF，用来 <BR>导入和导出LDAP目录的记录项。 <BR><BR>dn: uid=fsmith, ou=employees, dc=foobar, dc=com <BR><BR>objectclass: person <BR><BR>objectclass: organizationalPerson <BR><BR>objectclass: inetOrgPerson <BR><BR>objectclass: foobarPerson <BR><BR>uid: fsmith <BR><BR>givenname: Fran <BR><BR>sn: Smith <BR><BR>cn: Fran Smith <BR><BR>cn: Frances Smith <BR><BR>telephonenumber: 510-555-1234 <BR><BR>roomnumber: 122G <BR><BR>o: Foobar, Inc. <BR><BR>mailRoutingAddress: fsmith@foobar.com <BR><BR>mailhost: mail.foobar.com <BR><BR>userpassword: {crypt}3x1231v76T89N <BR><BR>uidnumber: 1234 <BR><BR>gidnumber: 1200 <BR><BR>homedirectory: /home/fsmith <BR><BR>loginshell: /usr/local/bin/bash <BR><BR>属性的值在保存的时候是保留大小写的，但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性 <BR>（例如，password）在搜索的时候需要区分大小写。 <BR><BR>让我们一点一点地分析上面的记录项。 <BR><BR>dn: uid=fsmith, ou=employees, dc=foobar, dc=com <BR><BR>这是Fran的LDAP记录项的完整DN，包括在目录树中的完整路径。LDAP（和X.500）使用uid（User ID），不要 <BR>把它和UNIX的uid号混淆了。 <BR><BR>objectclass: person <BR><BR>objectclass: organizationalPerson <BR><BR>objectclass: inetOrgPerson <BR><BR>objectclass: foobarPerson <BR><BR>可以为任何一个对象根据需要分配多个对象类型。person对象类型要求cn（common name）和sn（surname） <BR>这两个域不能为空。persion对象类型允许有其它的可选域，包括givenname、telephonenumber，等等。 <BR>organizational Person给person加入更多的可选域，inetOrgPerson又加入更多的可选域（包括电子邮件信息）。 <BR>最后，foobarPerson是为Foobar定制的对象类型，加入了很多定制的属性。 <BR><BR>uid: fsmith <BR><BR>givenname: Fran <BR><BR>sn: Smith <BR><BR>cn: Fran Smith <BR><BR>cn: Frances Smith <BR><BR>telephonenumber: 510-555-1234 <BR><BR>roomnumber: 122G <BR><BR>o: Foobar, Inc. <BR><BR>以前说过了，uid表示User ID。当看到uid的时候，就在脑袋里想一想“login"。 <BR><BR>请注意CN有多个值。就象上面介绍的，LDAP允许某些属性有多个值。为什么允许有多个值呢？假定你在用 <BR>公司的LDAP服务器查找Fran的电话号码。你可能只知道她的名字叫Fran，但是对人力资源处的人来说她的 <BR>正式名字叫做Frances。因为保存了她的两个名字，所以用任何一个名字检索都可以找到Fran的电话号码、 <BR>电子邮件和办公房间号，等等。 <BR><BR>mailRoutingAddress: fsmith@foobar.com <BR><BR>mailhost: mail.foobar.com <BR><BR>就象现在大多数的公司都上网了，Foobar用Sendmail发送邮件和处理外部邮件路由信息。Foobar把所有用户 <BR>的邮件信息都存在LDAP中。最新版本的Sendmail支持这项功能。 <BR><BR>Userpassword: {crypt}3x1231v76T89N <BR><BR>uidnumber: 1234 <BR><BR>gidnumber: 1200 <BR><BR>gecos: Frances Smith <BR><BR>homedirectory: /home/fsmith <BR><BR>loginshell: /usr/local/bin/bash <BR><BR>注意，Foobar的系统管理员把所有用户的口令映射信息也都存在LDAP中。FoobarPerson类型的对象具有这 <BR>种能力。再注意一下，用户口令是用UNIX的口令加密格式存储的。UNIX的uid在这里为uidnumber。提醒你一下， <BR>关于如何在LDAP中保存NIS信息，有完整的一份RFC。在以后的文章中我会谈一谈NIS的集成。 <BR><BR>LDAP复制 <BR><BR>LDAP服务器可以使用基于“推”或者“拉”的技术，用简单或基于安全证书的安全验证，复制一部分或者所 <BR>有的数据。 <BR><BR>例如，Foobar有一个“公用的”LDAP服务器，地址为ldap.foobar.com，端口为389。Netscape Communicator <BR>的电子邮件查询功能、UNIX的“ph"命令要用到这个服务器，用户也可以在任何地方查询这个服务器上的员工 <BR>和客户联系信息。公司的主LDAP服务器运行在相同的计算机上，不过端口号是1389。 <BR><BR>你可能即不想让员工查询资产管理或食谱的信息，又不想让信息技术人员看到整个公司的LDAP目录。为了解决 <BR>这个问题，Foobar有选择地把子目录树从主LDAP服务器复制到“公用”LDAP服务器上，不复制需要隐藏的信息。 <BR>为了保持数据始终是最新的，主目录服务器被设置成即时“推”同步。这些种方法主要是为了方便，而不是安全， <BR>因为如果有权限的用户想查询所有的数据，可以用另一个LDAP端口。 <BR><BR>假定Foobar通过从奥克兰到欧洲的低带宽数据的连接用LDAP管理客户联系信息。可以建立从ldap.foobar.com:1389 <BR>到munich-ldap.foobar.com:389的数据复制，象下面这样： <BR><BR>periodic pull: ou=asia,ou=customers,o=sendmail.com <BR><BR>periodic pull: ou=us,ou=customers,o=sendmail.com <BR><BR>immediate push: ou=europe,ou=customers,o=sendmail.com <BR><BR>“拉”连接每15分钟同步一次，在上面假定的情况下足够了。“推”连接保证任何欧洲的联系信息发生了变化就 <BR>立即被“推”到Munich。 <BR>用上面的复制模式，用户为了访问数据需要连接到哪一台服务器呢？在Munich的用户可以简单地连接到本地服务 <BR>器。如果他们改变了数据，本地的LDAP服务器就会把这些变化传到主LDAP服务器。然后，主LDAP服务器把这些变化 <BR>“推”回本地的“公用”LDAP服务器保持数据的同步。这对本地的用户有很大的好处，因为所有的查询（大多数是读）都在本地的服务器上进行，速度非常快。当需要改变信息的时候，最终用户不需要重新配置客户端的软件，因为LDAP目录服务器为他们完成了所有的数据交换工作。 <img src ="http://www.blogjava.net/kapok/aggbug/4036.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-05 16:08 <a href="http://www.blogjava.net/kapok/archive/2005/05/05/4036.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenLDAP快速指南</title><link>http://www.blogjava.net/kapok/archive/2005/05/05/4034.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 05 May 2005 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/05/4034.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4034.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/05/4034.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4034.html</trackback:ping><description><![CDATA[<A href="http://www.trainlinux.com/n/2002-05-06/5034.html">http://www.trainlinux.com/n/2002-05-06/5034.html</A><BR><BR>原文作者：Jason P. Holland 27-February-2001 <BR>翻译： ideal Linuxaid专业技术支持网站 <BR>LDAP简介 <BR>LDAP（轻量级目录访问协议，Lightweight Directory Access Protocol)是实现提供被称为目录服务的信息服务。 <BR>目录服务是一种特殊的数据库系统，其专门针对读取，浏览和搜索操作进行了特定的优化。目录一般用来包含描 <BR>述性的，基于属性的信息并支持精细复杂的过滤能力。目录一般不支持通用数据库针对大量更新操作操作需要的 <BR>复杂的事务管理或回卷策略。而目录服务的更新则一般都非常简单。这种目录可以存储包括个人信息、web链结、 <BR>jpeg图像等各种信息。为了访问存储在目录中的信息，就需要使用运行在TCP/IP之上的访问协议—LDAP。 <BR><BR>LDAP目录中的信息是是按照树型结构组织，具体信息存储在条目(entry)的数据结构中。条目相当于关系数据库中 <BR>表的记录；条目是具有区别名DN（Distinguished Name）的属性（Attribute），DN是用来引用条目的，DN相当于 <BR>关系数据库表中的关键字（Primary Key）。属性由类型（Type）和一个或多个值（Values）组成，相当于关系数 <BR>据库中的字段（Field）由字段名和数据类型组成，只是为了方便检索的需要，LDAP中的Type可以有多个Value， <BR>而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置 <BR>和组织关系进行组织，非常的直观。LDAP把数据存放在文件中，为提高效率可以使用基于索引的文件数据库，而不 <BR>是关系数据库。类型的一个例子就是mail，其值将是一个电子邮件地址。 <BR><BR><BR><BR>LDAP的信息是以树型结构存储的，在树根一般定义国家(c=CN)或域名(dc=com)，在其下则往往定义一个或多个组织 <BR>(organization)(o=Acme)或组织单元(organizational units) (ou=People)。一个组织单元可能包含诸如所有雇员、 <BR>大楼内的所有打印机等信息。此外，LDAP支持对条目能够和必须支持哪些属性进行控制，这是有一个特殊的称为对 <BR>象类别(objectClass)的属性来实现的。该属性的值决定了该条目必须遵循的一些规则，其规定了该条目能够及至少 <BR>应该包含哪些属性。例如：inetorgPerson对象类需要支持sn(surname)和cn(common name)属性，但也可以包含可选 <BR>的如邮件，电话号码等属性。 <BR><BR>目录设计 <BR>设计目录结构是LDAP最重要的方面之一。下面我们将通过一个简单的例子来说明如何设计合理的目录结构。该例子将 <BR>通过Netscape地址薄来访文。假设有一个位于美国US(c=US)而且跨越多个州的名为Acme(o=Acme)的公司。Acme希望为 <BR>所有的雇员实现一个小型的地址薄服务器。 <BR>我们从一个简单的组织DN开始：　 <BR><BR>dn: o=Acme, c=US <BR>Acme所有的组织分类和属性将存储在该DN之下，这个DN在该存储在该服务器的目录是唯一的。Acme希望将其雇员的信 <BR>息分为两类：管理者(ou=Managers)和普通雇员(ou=Employees),这种分类产生的相对区别名(RDN,relative distinguished <BR>names。表示相对于顶点DN)就shi ： <BR><BR>dn: ou=Managers, o=Acme, c=US <BR>dn: ou=Employees, o=Acme, c=US <BR>在下面我们将会看到分层结构的组成：顶点是US的Acme，下面是管理者组织单元和雇员组织单元。因此包括Managers <BR>和Employees的DN组成为： <BR><BR>dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US <BR>dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US <BR>dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US <BR>为了引用Jason H. Smith的通用名(common name )条目，LDAP将采用cn=Jason H. Smith的RDN。然后将前面的父条目 <BR>结合在一起就形成如下的树型结构： <BR><BR>cn=Jason H. Smith <BR>+ ou=Managers <BR>+ o=Acme <BR>+ c=US <BR>-&gt; cn=Jason H. Smith, ou=Managers, o=Acme, c=US <BR><BR>现在已经定义好了目录结构，下一步就需要导入目录信息数据。目录信息数据将被存放在LDIF文件中，其是导入目录 <BR>信息数据的默认存放文件。用户可以方便的编写Perl脚本来从例如/etc/passwd、NIS等系统文件中自动创建LDIF文件。 <BR><BR>下面的实例保存目录信息数据为testdate.ldif文件，该文件的格式说明将可以在man ldif中得到。 <BR><BR>在添加任何组织单元以前，必须首先定义Acme DN：　 <BR><BR>dn: o=Acme, c=US <BR>objectClass: organization <BR>这里o属性是必须的 <BR><BR>o: Acme <BR>下面是管理组单元的DN，在添加任何管理者信息以前，必须先定义该条目。 <BR><BR>dn: ou=Managers, o=Acme, c=US <BR>objectClass: organizationalUnit <BR>这里ou属性是必须的。 <BR><BR>ou: Managers <BR>第一个管理者DN： <BR><BR>dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US <BR>objectClass: inetOrgPerson <BR>cn和sn都是必须的属性： <BR><BR>cn: Jason H. Smith <BR>sn: Smith <BR>但是还可以定义一些可选的属性： <BR><BR>telephoneNumber: 111-222-9999 <BR>mail: headhauncho@acme.com <BR>localityName: Houston <BR><BR>可以定义另外一个组织单元： <BR><BR>dn: ou=Employees, o=Acme, c=US <BR>objectClass: organizationalUnit <BR>ou: Employees <BR>并添加雇员信息如下： <BR><BR>dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US <BR>objectClass: inetOrgPerson <BR>cn: Ray D. Jones <BR>sn: Jones <BR>telephoneNumber: 444-555-6767 <BR>mail: jonesrd@acme.com <BR>localityName: Houston <BR><BR>dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US <BR>objectClass: inetOrgPerson <BR>cn: Eric S. Woods <BR>sn: Woods <BR>telephoneNumber: 444-555-6768 <BR>mail: woodses@acme.com <BR>localityName: Houston <BR>安装配置 <BR>下一步需要设置OpenLDAP来接受刚才定义的目录结构的导入及提供访问Netscape中的地址薄。在OpenLDAP邮件列 <BR>表中一个常见的问题是“我如何使Netscape地址薄来使用我的LDAP服务器？”保存地址薄信息是LDAP常见的一个 <BR>应用方面，这是因为它具有快速的查询和读取功能。而且OpenLDAP支持例如SSL/TLS等会话加密和目录服务器复制 <BR>等功能，这样就可以实现一个非常好的开发源码解决方案。 <BR><BR>下面的讨论都是基于openldap-2.0.7，其支持LDAP v2和LDAP v3。LDAP v3相对于LDAP v2最重要的是添加了对传输 <BR>层安全(TLS,Transport Layer Security)的支持及增加了认证方法。OpenLDAP有两种安装方式：源代码方式和打包 <BR>的deb/rpm模式。可以从http://www.openldap.org/下载源代码方式或者从http://rpmfind.net/及光盘上得到RPM包 <BR>方式。源代码方式安装过程如下： <BR><BR>[root@radiusd src]# ar -xzvf openldap-2.0.7.tgz <BR><BR>[root@radiusd src]# cd openldap-2.0.7 <BR><BR>[root@radiusd openldap-2.0.7]# ./configure --prefix=/usr/local <BR><BR>这里指示openldap被安装在/usr/local目录下，当这并不是必须的。 <BR><BR>[root@radiusd openldap-2.0.7]# make depend;make <BR><BR>在安装结束以前进行测试： <BR><BR>[root@radiusd openldap-2.0.7]# make test <BR><BR>[root@radiusd openldap-2.0.7]# make install <BR><BR>若出现任何编译错误，应该到OpenLDAP邮件列表去寻求帮助。你也许需要在PATH环境变量中添加如下路径： <BR>/usr/local/libexec, /usr/local/bin及/usr/local/sbin。 <BR><BR>PRM包方式的安装实例如下： <BR><BR>rpm －ivh openldap-2.0.7-14-i386.rpm <BR><BR>rpm －ivh openldap-devel-2.0.7-14-i386.rpm <BR><BR>下来需要编辑slapd.conf文件，其是slapd守护进程的配置文件。slapd进程负责响应客户应用访问目录服务请求。配置 <BR>文件存放/usr/local/etc/openldap。 <BR><BR>为了能使用Netscape地址薄属性，需要添加一些额外的"模式"配置信息。在slapd.conf文件的开头处添加如下include <BR>内容，但是根据安装路径的不同，模式目录路径可能也不大一样。 <BR><BR>include /usr/local/etc/openldap/schema/cosine.schema <BR>include /usr/local/etc/openldap/schema/inetorgperson.schema <BR>在slapd.conf的定义的suffix和rootdn行修改为能反应你需要的DN： <BR><BR>suffix "o=Acme, c=US" <BR>rootdn "cn=root, o=Acme, c=US" <BR>这里cn=root条目是我们的管理DN，其不受任何访问控制或限制。其默认是cn=Manager，但是我希望root访问。在 <BR>slapd.conf文件的末端添加如下内容，实现给Netsacpe进行目录过滤和搜索操作的读权限。所有没有授权的访问目 <BR>录服务的请求都被作为匿名用户对待。下面的DN条目被格式化处理，也就是所有的空格被去掉，并且其值被逗号隔开。 <BR>在访问控制，必须格式化条目否则将不能工作。 <BR><BR>access to dn=".*,o=Acme,c=US" <BR>by anonymous read <BR>对目录的访问许可以进行精细的调节以适应各种需求。OpenLDAP 2.0管理指南有非常好的配置访问许可的文档说明。 <BR>这里为了测试目的，这样的访问控制级别是足够了。 <BR><BR>下面我们就将启动slapd服务器。若系统的ldap是通过RPM/DEB格式进行安装的，根据使用的Linux发布版本不同，启动 <BR>脚本可能是/etc/rc.d/init.d/ldap或/etc/init.d/ldap。当然也可以手工启动来进行测试。 <BR><BR>slapd &amp; <BR><BR>下面测试看slapd是否在运行 <BR><BR>ps -ef | grep -i slapd | grep -v grep <BR>root 15479 1 0 10:42 ? 00:00:00 slapd <BR>root 15483 15479 0 10:42 ? 00:00:00 slapd <BR>root 15484 15483 0 10:42 ? 00:00:00 slapd <BR>root 15491 15483 0 10:43 ? 00:00:00 slapd <BR>root 15492 15483 0 10:43 ? 00:00:00 slapd <BR><BR>下面测试ldap的默认端口389是否被监听： <BR><BR>netstat -an | grep 389 <BR>tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN <BR><BR>到这里为止，一切看上去都很正常，下面将导入ldap信息数据到数据库中： <BR><BR>ldapadd -D "cn=root, o=Acme, c=US" -W -v -f testdata.ldif <BR><BR>我们使用-D参数和无限制的cn来捆绑目录，这样允许写信息到目录中。-W参数导致服务器需要密码才能访问。缺省的 <BR>密码是在slapd.conf文件中的rootpw来设定的，默认是secre。使用该默认密码是非常危险的，因此在测试完毕以后， <BR>应该改变该密码。记得使用-v参数来进行详细输出以判断是否及如何修正出现的错误。 <BR><BR>测试　 <BR>当数据导入结束，下一步就需要配置客户端来进行测试。Netscape地址薄支持很多目录属性，在下面的资源部分将包 <BR>含Netscape地址薄API标准链结地址。下面的简单的测试实例，将使用如下属性：cn,sn,mail.telephoneNumber和 <BR>localityName。地址薄中的Nickname条目是通过属性xmozillanickname来支持的，其在任何“模式”中都不是默认地 <BR>被支持而需要对“模式”进行修改。本文将不设计如何修改“模式”方面。 <BR>打开Netscape的地址薄，选择File-&gt;New Directory，输入LDAP服务器的信息： <BR><BR>Description: Acme Address Book <BR>LDAP Server: the IP/hostname address of your LDAP server <BR>Server Root: o=Acme, c=US <BR><BR>端口号和其他信息不需要修改。而且由于链结将以匿名用户身份进行，因此不需要设置用户名和密码。 <BR><BR>选择OK按钮，然后在左边的目录栏选中"Acme Address Book"，最后在"Show names containing"框中输入一个查询， <BR>例如Smith然后回车。你将可以看到返回了一行数据。 <BR><BR>若希望对每个组织单元得到独立的列表输出，你可以在Netsacpe中创建另外一个新的目录条目： <BR><BR>Description: Acme Managers <BR>LDAP Server: the IP/hostname address of your LDAP server <BR>Server Root: ou=Managers, o=Acme, c=US <BR><BR>这将导致仅仅在Acem目录中搜索Nanagers组织单元，也就是实现了一定的过滤。当然可以对Employees进行同样的限制。 <BR><BR>错误处理 <BR>可能会在测试中遇到如下问题： <BR>若目录服务不能返回数据，则编辑slapd.conf file并添加"Loglevel 1"。将导致slapd服务进程记录所有的信息到 <BR>syslog LOCAL4。同样需要编辑 /etc/syslog.conf文件来将这些信息定向到一个单独的文件来便于调试。检查该 <BR>log文件以确保slapd服务器启动正常没有任何错误信息。这同样会详细记录每个请求服务的信息。 <BR>确保PATH环境包括所有的ldap命令的路径。 <BR>若导入数据失败，仔细察看文件LDIF文件格式。更高一级的条目必须首先出现，从你的目录数顶端开始，直到叶子节点。 <BR>需要有root身份来启动slapd，除非改变slapd到超过1024以上的端口。 <BR>检查slapd.conf文件格式，若你的访问控制列表没有被格式化，则可能导致链结服务器失败。 <BR>使用Netscape地址薄来访问LDAP是掌握使用LDAP概念一个非常好的方法。下面是一些和LDAP相关的一些链结资源， <BR>包括一些使用LDAP认证一些常见服务的方法如：系统登录及Samba等。 <BR><BR>资源 <BR>http://www.openldap.org/ - OpenLDAP Web Site <BR>http://www.openldap.org/doc/admin/ - OpenLDAP 2.0 Administrators Guide <BR>http://www.hklc.com/ldapschema/ - LDAP Schema Browser <BR>http://www.padl.com/pam_ldap.html - Pam-LDAP Authentication Module (they also have some Perl migration scripts) <BR>http://perl-ldap.sourceforge.net/ - Perl LDAP modules <BR>http://www.unav.es/cti/ldap-smb-howto.html - Samba-PDC LDAP Howto <BR>http://developer.netscape.com/docs/manuals/communicator/addrapi.htm - Netscape Address Book API Specification <BR><img src ="http://www.blogjava.net/kapok/aggbug/4034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-05 15:34 <a href="http://www.blogjava.net/kapok/archive/2005/05/05/4034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>