kapok

垃圾桶,嘿嘿,我藏的这么深你们还能找到啊,真牛!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  455 随笔 :: 0 文章 :: 76 评论 :: 0 Trackbacks
http://dev2dev.bea.com.cn/techdoc/wlportal/20030625.html
本文将会告诉你如何在BEA WebLogic Portal中使用统一用户配置文件(Unified User Profile)。本文通过一些必要的编程以及配置步骤的介绍来告诉读者如何利用门户的内置规则引擎结合外部的用户档案数据来提供一个个性化的门户应用。


BEA WebLogic Portal运行在WebLogic Server之上,并且拥有一系列的特性和服务,从而使你能够建立一个交互式的、自适应的门户网站。它的一个突出优点是提供了一些能够基于用户档案文件来配置网站的工具。例如,仅当用户档案文件符合特定要求时,你才能够获得授权,从而进入特定的门户页面,甚至是portlets,或者,能够修改门户页面提供的内容或基于用户档案文件的portlet。例如,一个用户可能需要成为某个部门的成员或是在公司中占据特定的职位才能够进入不同的内容条目或是portlet。这可以利用在BEA EBCC(E-Business Control Center)中那些以交互方式定义的的规则来实现。使用这个工具,可以很容易地把工作内容或是市场活动瞄准到特定的用户,并且不需要高深的编程知识。另一方面,作为软件开发人员,你可以享受到如下的好处:利用方便的Java API和Taglib就能够访问并且修改用户档案文件。

不幸的是,数据很少被仅仅存储在企业的一个地方,而且也很少被保存为BEA WebLogic Portal所需要的格式。

实际上,用户档案数据可能被分散地存储在很多不同的系统当中。通常,数据的一部分被存储在像SAP这样的ERP系统中、像Siebel这样的CRM系统中、LDAP目录服务中、像Oracle或 Sybase这样的关系数据库中,以及各种各样其他的地方。幸运的是,BEA Portal没有仅在它的本地数据库模式中提供用户描述的存储。它定义了名为统一用户档案文件(Unified User Profile ,UUP)的机制,使你可以创建所有的与该门户应用(portal application)紧密结合用户档案文件的应用模块。接着,你就能创建内容选取规则(content selection rule),或是用户权限,从而利用那些已经存在并且安全地存储于企业遗留或是核心系统中的数据。


创建统一用户档案

本文将会告诉你如何建立并使用一个依赖于外部数据的UUP。我们假设现在有一个大型跨国公司的员工门户。简单地说,我们只考虑一个门户页面,它包括了一个展示公司新闻的portlet和一个展示公司国际分支机构销售数据的portlet。后者仅仅对于销售部门的员工是可见的。新闻portlet根据员工所在的部门、员工的国籍、以及员工在公司内职位的不同来显示公司新闻。有些新闻将会仅仅和软件开发人员相关,因此仅仅对于他们可见,另外一些可能仅仅对于管理人员是可见的。当然,任何信息都可以按照员工首选的语言来显示。

公司已经在不同的数据库中存储了关于员工的大多数数据。值得庆幸的是,该公司实施了面向服务的软件体系结构,所以所有的相关员工数据都能够以像清单1中定义的SOAP Web service的方式被访问。(清单1-3附后

为了使用来自门户的Web服务中的数据,必须首先使用EBCC定义用户档案文件和相关规则。用户档案文件在"site infrastructure"选项卡中建立。按照通常的经验,你应该为每一个要访问的数据源创建至少一个用户档案文件。为了满足要求,你会想要定义两个用户档案文件。第一个是corporateProfile,如图1所示。这个配置文件稍后将会被映射成从Web服务中获得的数据。如果你检查了Web服务的WSDL,将会注意到它没有定义员工的首选语言。因此,你需要创建另外一个名为preferencesData的配置文件,它将被保存在默认的门户数据库模式中。


接下来,你需要建立一些规则(rules),稍候它们将被应用到用户档案文件上。首先,定义为用户选取特定新闻内容的规则。你需要在EBCC的presentation选项卡中定义一个新的内容选择器。现在,可以定义内容选取规则了。


在定义这些规则时,EBCC会连接到你的门户应用,从而获取可用的内容类型及其元数据。当然,可否获取元数据取决于那些被载入到你门户模式中的内容。出于演示的目的,BEA提供了一个叫做bulk loader的工具。可以使用该工具从具有特殊注解的HTML文件中索引和装载内容。一旦检索出了元数据,你就能够借助可利用的菜单创建一个内容搜索。

图2所示的是组成内容搜索的规则。它只选取了那些符合国家、部门、职务、和用户喜好语言条件的内容条目。把规则用"ShowNews"这个名字进行保存,以便以后在新闻portlet中访问它。使用相似的方法,可以创建一个权限片断,其中包含了所有在销售部门工作的用户,我们把它叫做MemberOfSales。在保持同步的同时,这些规则已经可以在门户应用中使用了。


开发UUP

现在开始真正的软件开发工作。针对那些可以获得预期结果的规则,你需要提供能够在Web 服务和门户应用之间连接用户档案文件数据的组件。访问用户档案文件的一般结构如图3所示。在BEA WebLogic Portal中,所有对用户档案文件的访问都是通过一个名为UserManager的无状态会话bean实现的。


根据用户档案文件的类型,它授权了对适当ProfileManager EJB的访问。接着,ProfileManager EJB根据被请求的用户档案文件,授权对一个com.bea.p13n.property.EntityPropertyManager类型的无状态会话bean 的访问。这就是实际的用户档案文件访问过程(参见图3)。框架中展示了两个钩子(hook),你可以在这里插入你自己的配置文件访问。只在很少的情况下,你需要为特定的配置文件类型编写一个自定义的ProfileManager。如果你需要提供一个和默认实现功能完全不同的实现,这可以作为你的一个选择。大多数情况下,你希望提供一个自定义的EntityPropertyManager,并且为一个定义在EBCC中的,特定的用户档案文件而注册它。本文余下的部分将会专注于后面这种情况。









EBCC中定义的用户档案文件

你需要定义一个其远程接口为EntityPropertyManage类型的EJB,并且把它部署在BEA WebLogic Portal所在的EAR中,同时,把它注册到门户的用户管理工具中去。清单3展示了组成CorporateProfileManager这个EJB的所有的类。在我们的例子中,用户数据是只读的,不能被门户的配置文件管理工具所修改。在企业环境中这并不少见,这样的数据可能只有授权用户才能够编辑。


在这样的环境中,必须以非空的形式实现的EntityPropertyManager的方法是:

public Object getProperty(PropertyLocator propertyLocator,
String pSet, String pName) throws EntityNotFoundException;

public EntityPropertyCache getProperties(PropertyLocator propertyLocator)
throws EntityNotFoundException;

对于其他方法,你应该抛出一个如上面代码中所示的UnsupportedOperation异常。值的注意的是,把用户档案文件的检索结果缓冲一定的时间是一种很好的做法。同样,getProperty()方法通常把任务委托给getProperties() 方法。该方法一次性地为一个特定用户检索出用户档案数据,并且缓冲它们以便后面使用。为用户档案文件提供数据的Web服务由BEA WebLogic Workshop来创建。CorporateProfileManager使用由BEA WebLogic Workshop创建的stubs来访问隐藏了所有实现细节的服务。

EJB被打包在自己的jar文件中,并且被部署在门户所在的企业应用中。最后,你需要通知ProfileManager EJB:它应该委托新部署的CorporateProfileManager EJB,为所有向用户档案文件"corporateProfile"发出的请求提供服务,这可以通过在ProfileManager的配置描述文件中把用户档案文件映射到EJB来实现。ProfileManager位于usermgt.jar中。

你可以使用WebLogic控制台来编辑配置描述文件,或者采用从jar中提取文件,修改它们,然后重新打包成jar的方法。如同下面展示的那样,你需要添加一个新的封闭条目来映射CorporateProfileManager的配置文件。条目的名称必须符合PropertyMapping/<profileName>的格式,其中<profileName>是指EBCC中定义的用户档案文件的名字。你还需要针对刚刚建立的CorporateProfileManager ejb创建一个ejb-ref(ejb描述文件)(参见清单4)。


最后,你需要编辑weblogic-ejb-jar.xml文件,把在web.xml中建立的ejb-ref-name映射为如下所示的已部署的EJB的实际JNDI名字。

<weblogic-enterprise-bean>
<ejb-name>UserProfileManager</ejb-name>
<reference-descriptor>
<ejb-reference-description>
<ejb-ref-name>ejb/CorporateProfileManager</ejb-ref-name>
<jndiname>
.BEA_personalization.CorporateProfileManager
</jndi-name>
</ejb-reference-description>
</reference-descriptor>
</weblogic-enterprise-bean>

当应用了这些修改之后,你需要做的仅仅是重新部署企业应用。现在你的用户档案文件已经可以使用了。


运行状态的UUP
现在,我们来看看新的用户档案文件如何运行。假设有两个员工:Sima是在德国法兰克福工作的开发部经理;Teresa是在英国伦敦工作的销售部职员。

Sima的首选语言是德语,Teresa的首选语言却是英语。

为了测试配置文件是否按照你所要求的那样被访问,你可以使用WebLogic Portal的management WebApp。选择user management->users,然后选择一个用户。为这个用户选择corporateProfile作为察看属性。如果一切工作正常,你应该看到从Web服务中检索出来的实际的数值。(参考图4)现在,使用EBCC portlet创建向导创建并且部署你自己的portlets。利用在EBCC中定义好的内容选择器来选择要显示给用户的合适的新闻内容。清单3显示了一个简单的新闻portlet。实际执行内容选择器的代码如下所示:

<pz:contentSelector rule="ShowNews"
contentHome="<%=ContentHelper.DEF_DOCUMENT_MANAGER_HOME %>"
id="news"/>


需要注意的是我们所使用的实际规则既访问了preferencesData,也访问了corporateProfile,从而获取数据。实际上,我们可以把不同来源的数据混合到一起,从中选择内容或是建立用户访问权限。在portalTools Web application中,你需要使portlet可用并且可见。同样,你需要在销售portlet上应用管理权限片断。(参见图5)



图6a和6b分别展示了用户Teresa和Sima的门户显示。





正如期望的那样,既然Teresa工作在销售部门,她就看到销售数据的portlet。她的所有新闻条目都是按照她配置文件设定的那样,用英文显示。Sima看不到销售数据,因为她不是销售部门的一员,而且她不具备察看销售portlet的权限。她的新闻条目都是德语的,她还可以看到许多不同的新闻条目,因为她在开发部门工作。

小结
到目前为止,你已经掌握并且理解了在BEA WebLogic Portal中建立UUP的基本机制。通过部署和注册适当的EntityPropertyManager EJB你可以集成任何数目的遗留数据源,基于此,你能够创建用户档案文件。而且你还能--在你所能做的其他事情中--在你的门户应用中提供个性化的内容和细粒度的访问控制。

当然,在实际的项目中还必须对实际的EJB设计进行其他重要的考虑。例如,你的后端数据源不像你所要求的那样可用并且可靠。在这种情况下,你可能会创建一个本地持久缓冲。这个缓冲可能存在的时间非常短而且可能仅仅作为备用数据,以防真正提供数据的系统掉线。

你还可以使用BEA 缓冲库(BEA caching library)为缓冲EntityPropertyManager中的数据提供更好的控制。
考虑到未来的开发,你还应该关注一下BEA提供的另外一个工具。WebLogic Liquid Data是专门为集成来自不同数据源的数据而设计的。插入到WebLogic Portal之后,WebLogic Liquid Data将会使实施用户档案文件的过程更加容易和迅速。


清单 1: web服务 WSDL

<?xml version="1.0" encoding="utf-8"?>
<!-- -info:link source="UserInfoService.jws" autogen="true" -->

<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:conv="http://www.openuri.org/2002/04/soap/conversation/"
xmlns:cw="http://www.openuri.org/2002/04/wsdl/conversation/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:jms="http://www.openuri.org/2002/04/wsdl/jms/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:s0="http://www.openuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xm="http://www.bea.com/2002/04/xmlmap/"
targetNamespace="http://www.openuri.org/" >
<types>
<s:schema attributeFormDefault="qualified" elementFormDefault="qualified"
targetNamespace="http://www.openuri.org/">
<s:element name="getUserInfo">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="userId" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="getUserInfoResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="getUserInfoResult" type="s0:UserInfo" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="UserInfo">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="address" type="s0:Address" />
<s:element minOccurs="0" maxOccurs="1" name="person" type="s0:Person" />
<s:element minOccurs="0" maxOccurs="1" name="orgaInfo" type="s0:OrgaInfo" />
</s:sequence>
</s:complexType>
<s:complexType name="Address">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="street" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="city" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="postcode" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="country" type="s:string" />
</s:sequence>
</s:complexType>
<s:complexType name="Person">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="firstNames" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="lastNames" type="s:string" />
</s:sequence>
</s:complexType>
<s:complexType name="OrgaInfo">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="department" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="function" type="s:string" />
</s:sequence>
</s:complexType>
<s:element nillable="true" name="UserInfo" type="s0:UserInfo" />
</s:schema>
</types>
<message name="getUserInfoSoapIn">
<part name="parameters" element="s0:getUserInfo" />
</message>
<message name="getUserInfoSoapOut">
<part name="parameters" element="s0:getUserInfoResponse" />
</message>
<portType name="UserInfoServiceSoap">
<operation name="getUserInfo">
<input message="s0:getUserInfoSoapIn" />
<output message="s0:getUserInfoSoapOut" />
</operation>
</portType>
<binding name="UserInfoServiceSoap" type="s0:UserInfoServiceSoap">
<soap:binding transport=
"http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="getUserInfo">
<soap:operation soapAction=
"http://www.openuri.org/getUserInfo" style="document"/>
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<service name="UserInfoService">
<port name="UserInfoServiceSoap" binding="s0:UserInfoServiceSoap">
<soap:address location=
"http://C800-001:7501/userInfoService/UserInfoService.jws"/>
</port>
</service>
</definitions>

清单 2: CorporateProfileManager

/**
* The remote interface of the ejb that will access the corporateProfile
*/
package com.iternum.uup;

public interface CorporateProfileManager extends com.bea.p13n.property.EntityPropertyManager {

}

/**
* Create the implementation of the CorporateProfileManager. This class
* does not support dynamic properties nor any write operations and will
* throw UnsupportedOperationException accordingly
* iternum GmbH (bankkar)
*/
package com.iternum.uup;

import com.bea.p13n.property.*;
import com.bea.p13n.property.internal.PropertyMapKeyImpl;
import com.bea.p13n.property.internal.EntityPropertyCacheImpl;

import weblogic.jws.proxies.*;
import java.rmi.RemoteException;
import java.io.IOException;
import java.util.HashMap;

import org.openuri.www.UserInfo;

import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;

public class CorporateProfileManagerImpl implements javax.ejb.SessionBean {

private HashMap caches = new HashMap();
private SessionContext context;

public CorporateProfileManagerImpl() {
}

/**
*
* propertyLocator
* pSet
* pName
*
* EntityNotFoundException
*/
public Object getProperty(PropertyLocator propertyLocator, String pSet, String pName)
throws EntityNotFoundException {
EntityPropertyCache cache = getProperties(propertyLocator);
return cache.get(new PropertyMapKeyImpl(pSet,pName));
}

/**
* TODO: Use application wide caching
* Returns all properties that are available for a given property locator.
* Caches the properties locally.
* propertyLocator
*
* EntityNotFoundException
*/
public EntityPropertyCache getProperties(PropertyLocator propertyLocator)
throws EntityNotFoundException {
EntityPropertyCache cache = (EntityPropertyCache)caches.get(propertyLocator.getPkString());
if (cache != null) {
return cache;
}
cache = new EntityPropertyCacheImpl();

PropertyMapKey nextKey = null;

String pSet = "corporateProfile";
try {
String userName = propertyLocator.getPkString();
UserInfoService service = new UserInfoService_Impl();
UserInfo info = service.getUserInfoServiceSoap().getUserInfo(userName);
cache.put(new PropertyMapKeyImpl(pSet,"firstnames"),info.getPerson().getFirstNames());
cache.put(new PropertyMapKeyImpl(pSet,"lastname"),info.getPerson().getLastNames());
cache.put(new PropertyMapKeyImpl(pSet,"street"),info.getAddress().getStreet());
cache.put(new PropertyMapKeyImpl(pSet,"postcode"),info.getAddress().getPostcode());
cache.put(new PropertyMapKeyImpl(pSet,"city"),info.getAddress().getCity());
cache.put(new PropertyMapKeyImpl(pSet,"country"),info.getAddress().getCountry());
cache.put(new PropertyMapKeyImpl(pSet,"division"),info.getOrgaInfo().getDepartment());
cache.put(new PropertyMapKeyImpl(pSet,"function"),info.getOrgaInfo().getFunction());
caches.put(propertyLocator.getPkString(),cache);
} catch (IOException e) {
e.printStackTrace();
//To change body of catch statement use Options | File Templates.
}
return cache;
}


public void removeProperties(PropertyLocator propertyLocator)
throws EntityNotFoundException {
throw new UnsupportedOperationException("Read only profile");
}
public Object removeProperty(PropertyLocator propertyLocator, String s, String s1)
throws EntityNotFoundException {
throw new UnsupportedOperationException("Read only profile");
}

public void setProperty(PropertyLocator propertyLocator, String s, String s1, Object o)
throws PropertyValidationException, EntityNotFoundException {
throw new UnsupportedOperationException("Read only profile");
}

public void removeEntity(PropertyLocator propertyLocator)
throws EntityNotFoundException {
throw new UnsupportedOperationException("Read only profile");
}

public long createUniqueId(String s, String s1)
throws ConfigurableEntityCreateException {
throw new UnsupportedOperationException("Read only profile");
}

/**
* Not supported
* EntityNotFoundException
*/
public String[] getDynamicProperties(PropertyLocator propertyLocator, String s)
throws EntityNotFoundException {
throw new UnsupportedOperationException();
}

/**
* Not supported
* EntityNotFoundException
*/
public long getUniqueId(String s, String s1) throws EntityNotFoundException {
throw new UnsupportedOperationException();
}

/**
* Not supported
* EntityNotFoundException
*/
public PropertyLocator getPropertyLocator(long l) throws EntityNotFoundException {
throw new UnsupportedOperationException();
}

/**
* Not supported
* EntityNotFoundException
*/
public String getHomeName(long l) throws EntityNotFoundException {
throw new UnsupportedOperationException();
}

/**
* Not supported
*/
public String[] getEntityNames(String s) throws RemoteException {
throw new UnsupportedOperationException();
}

public void setSessionContext(SessionContext sessionContext) throws EJBException {
context = sessionContext;
}

public void ejbRemove() throws EJBException {
}

public void ejbActivate() throws EJBException {
}

public void ejbPassivate() throws EJBException {
}

public void ejbCreate() throws CreateException {
}
}

/**
* Home interface for the coporate profile manager.
We just need a default create method here
* iternum GmbH (bankkar)
* : CorporateProfileManagerHome.java,v 1.1 2003/01/15 16:54:17 bankkar Exp 505BR>*/
package com.iternum.uup;

import javax.ejb.CreateException;
import java.rmi.RemoteException;

public interface CorporateProfileManagerHome extends javax.ejb.EJBHome {

public CorporateProfileManager create() throws CreateException, RemoteException;

}

清单 3: news portlet代码段

<%@ page import="com.bea.p13n.content.ContentHelper"%>
<%@ taglib uri="es.tld" prefix="es" %>
<%@ taglib uri="pz.tld" prefix="pz" %>
<%@ taglib uri="um.tld" prefix="um" %>
<%@ taglib uri="cm.tld" prefix="cm" %>

<h3>iternum latest news</h3>

<pz:contentSelector rule="ShowNews"
contentHome="<%=ContentHelper.DEF_DOCUMENT_MANAGER_HOME %>"
id="news"/>
<es:forEachInArray array="<%=news%>" id="newsItem"
type="com.bea.p13n.content.Content">
<p><cm:printDoc id="newsItem"/></p>
</es:forEachInArray>


清单 4: ejb-ref that references the Corporate Profile Manager EJB<session>

<ejb-name>UserProfileManager</ejb-name>
...
<env-entry>
<env-entry-name>PropertyMapping/corporateProfile</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>CorporateProfileManager</env-entry-value>
</env-entry>
...
<ejb-ref>
<description>ejb/CorporateProfileManager</description>
<ejb-ref-name>ejb/CorporateProfileManager</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>com.iternum.uup.CorporateProfileManagerHome</home>
<remote>com.iternum.uup.CorporateProfileManager</remote>
<ejb-link>CorporateProfileManager</ejb-link>
</ejb-ref>
...
</session>

参考文献:
  BEA WebLogic Portal Documentation: Implementing User Profiles: http://edocs.bea.com/wlp/docs70/dev/usrgrp.htm#998993
  BEA WebLogic Portal Documentation: Portal Content Management: http://edocs.bea.com/wlp/docs70/dev/conmgmt.htm#1018613

关于作者

Karl F. Banke是位于德国法兰克福的iternum GmbH公司的首席咨询专家和总经理。他的工作领域涉及J2EE和EJB体系结构、Web Services以及项目管理。
posted on 2005-06-06 14:56 笨笨 阅读(935) 评论(0)  编辑  收藏 所属分类: ALLWeblogic Portal

只有注册用户登录后才能发表评论。


网站导航: