探秘企业门户开发:Java Portlet入门

原创
开发 后端
简单来说,门户就是一个iGoogle或是myYahoo!这样的现代化页面。门户的实现基于Java Portlet技术,而这个技术也可以实现企业门户以及其他商业或个人网站。如果你在从事企业级开发而不了解Portlet,那么可以先从这篇Portlet入门着手。

【51CTO精选译文】当你访问iGoogle或是myYahoo!一类的门户时,是否会对这种个性化门户界面的实现方式感到好奇呢?实现这种“组件式”门户的技术叫做Portlet。随着Portlet相关规范的统一,这种技术现在也被用于企业内部网站(企业门户)以及其他商业或个人网站。下面,我们将进行一次简短的Portlet入门介绍与教程。

#t#

Java Portlet的历史

自2003年最初的JSR 168规范发布以来,Portlet开发在企业和开源社区中都获得了积极响应。2008年6月发布了JSR 286规范,标志着Portlet开发技术已经非常成熟。截至目前已经有不止20个开源Portlet容器和门户产品可用,如SUN的Liferay Portal、eXo Platform和Jakarta Pluto等,也有来自主流软件厂商的商业化产品,如Vignette Portal、IBM WebSphere Portal、Sun OpenPortal和Oracle Portal(以前叫做BEA WebLogic Portal)等。

Web门户基础

那么,什么是门户呢?传统的观点认为Web分为三类:Web网站,搜索引擎和门户。Web网站一般放置个人主页或公司主页,而搜索引擎是网络爬虫,它索引个人和企业网页,以便于人们搜索,门户就象一个大杂烩,将各种有关或无关的东西全部糅合到一块(目前许多搜索引擎如Yahoo.com和MSN也是门户)。随着门户的演变,出现了一些新的特征,如保存用户的参数设置和其它自定义信息,用户也可以配置门户记住他们的设置,如背景色,显示记录条数等。支持自定义可以让不同的用户拥有个性化的门户,每个人访问门户时界面显示的内容可能完全不一样,如A看到的是新闻和股票,B看到的是娱乐和天文学。如图1所示。

Yahoo门户:门户自定义让门户记住用户的参数设置 
图 1 Yahoo门户:门户自定义让门户记住用户的参数设置

经过自定义后,不同种类的信息掺和在一起形成一个非常现代化的页面,目前最流行的做法是在门户上放置多个矩形框,每个矩形框代表一个Portlet。Wikipedia将门户定义为“以统一的方式显示来自不同地方的信息”,将Portlet定义为“可插拔的用户界面组件”。

门户的目标就是为不同用户定制显示不同的Portlet,以满足用户个性化的需求,这样做可以粘住用户。经过这几年的发展,门户的应用已经扩大到企业内部中去了,包括内部门户,B2B等形式,如企业财务门户将各种财务信息聚合到一起,分别以Portlet形式展示,如投资组合、401K计划、信用卡、银行账户等,财务部门人员就可以一次性获得大量的财务数据。

企业门户和Portlet容器

那么门户和Portlet容器是什么关系呢?简答:门户是Portlet容器的容器。Portlet容器是根据门户提供的Portlet标准API实现的供Portlet运行的环境,依靠这个环境,或者说平台,Portlet可以被实例化,使用,最终被处理掉(destroyed)。Java Portlet容器不是象Servlet容器那样标准的独立的容器,相反,它是在Java Servlet容器上实现的,并会重用Java Servlet的功能。从技术角度来说,Portlet容器可以看作是Portlet和门户之间的接口。

早期的Web门户都是采用封闭式开发的,自家开发的Portlet只能在一个特定的Portlet容器中运行,不具有很好的兼容性,遇到新项目或需求变化,开发人员不得不重新修改Portlet代码。这种情况直到2003年SUN发布JSR 168规范后才得到改善,虽说这个规范也不完美,但它提供了一个标准Portlet API,定义了Portlet生命周期和其它重要属性。即使到了今天,很多Portlet和Portlet容器都仍然遵循JSR 168或2008年发布的JSR 286规范,凡遵循这些规范编写的Portlet几乎都有很好的移植性。

提示:IBM也开发了自家的WebSphere portal,并且公开了API,IBM的API和SUN的API很类似,但最新的版本中,IBM放弃了自家的API,完全遵循JSR 168和JSR 286规范了。

现代Portlet容器可以用来构建企业内部网站(企业门户),商业网站或个人网站,大多数都实现了开箱即用的功能,如国际化支持,工具和内容管理,基于角色的授权,单点登录(SSO)支持,搜索和标签支持等。图2显示了一个正在运行的Portlet容器示例。

Apache Jetspeed门户:包括一个日历Portlet 
图 2 Apache Jetspeed门户:包括一个日历Portlet

用户可以拖动日历Portlet的位置,如图3所示。

移动日历Portlet 
图 3 移动日历Portlet

#p#

开发一个Portlet

下面这部分将介绍如何进行简单的Portlet开发。首先创建一个标准的Java项目,然后创建一个portlet.xml文件,在这个文件中定义哪些Portlet对哪些容器有效,以及在实例化时需要使用哪些类,但这个文件并没有定义如何注册和识别Portlet。

图4显示了一个示例Portlet项目的目录结构。

Portlet项目结构示例 
图 4 Portlet项目结构示例

下面的portlet.xml定义了一个Portlet:

< ?xml version="1.0" encoding="UTF-8"?> 
< portlet-app xmlns=  
  "http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation=  
    "http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" 
    version="1.0"> 
    < portlet> 
      < portlet-name>QuickSearch< /portlet-name> 
      < portlet-class> 
        org.springframework.web.portlet.DispatcherPortlet  
      < /portlet-class> 
      < init-param> 
        < name>contextConfigLocation< /name> 
        < value>/WEB-INF/context/portlet/QuickSearchDefinition.xml< /value> 
      < /init-param> 
      < supports> 
        < mime-type>text/html< /mime-type> 
        < portlet-mode>view< /portlet-mode> 
      < /supports> 
      < portlet-info> 
        < title>Quick Search< /title> 
      < /portlet-info>        
    < /portlet>      
< /portlet-app> 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

从上面的内容可以看出portlet.xml指定contextConfigLocation为Spring类的初始化参数。

列表1显示了完整的contextConfigLocation文件的内容。

< ?xml version="1.0" encoding="UTF-8"?> 
< beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:aop="http://www.springframework.org/schema/aop" 
  xsi:schemaLocation="  
   http://www.springframework.org/schema/beans   
   http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
   http://www.springframework.org/schema/aop   
   http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
     
  < bean id="quickEntitySearchController"   
    class="com.portlet.controller.QuickSearchController"   
    parent="basePageController">   
    < property name="sessionForm">< value>true< /value>< /property>   
 
    < !-- Keep command object throughout session --> 
    < property name="commandName" value="commandObject"/> 
    < property name="commandClass"   
      value="com.portlet.command.commandObject"/> 
    < property name="formView">< value>quick.search< /value>< /property> 
    < property name="successView">< value>quick.search< /value>< /property> 
    < property name="bindOnNewForm">< value>true< /value>< /property>       
    < property name="quickServiceClient" ref="quickServiceClient"/> 
  < /bean>     
     
  < bean id="portletModeParameterHandlerMapping" class="  
    org.springframework.web.portlet.handler.  
    PortletModeParameterHandlerMapping"> 
    < property name="order" value="10"/> 
    < property name="interceptors"> 
      < list> 
        < ref bean="parameterMappingInterceptor" /> 
      < /list> 
    < /property> 
 
    < property name="portletModeParameterMap"> 
      < map> 
        < entry key="view"> 
          < map> 
            < entry key="basePageAction"> 
              < ref bean="quickSearchController"/> 
            < /entry> 
          < /map> 
        < /entry> 
      < /map> 
    < /property> 
  < /bean> 
     
  < bean id="portletModeHandlerMapping" class=  
    "org.springframework.web.portlet.handler.PortletModeHandlerMapping"> 
    < property name="interceptors"> 
      < list> 
        < ref bean="parameterMappingInterceptor" /> 
      < /list> 
    < /property> 
    < property name="portletModeMap"> 
      < map> 
        < entry key="view">< ref bean="quickSearchController"/>< /entry> 
      < /map> 
    < /property> 
  < /bean> 
< /beans> 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.

接下来就是编写Java代码实现控制器,视图和Portlet处理程序了。视图是一个JSP页面,控制器和Portlet处理程序是Java类。在控制器和处理程序的帮助下,从不同数据源提取数据,如Web Service,数据库或feed等,你可以通过命令模式将这些数据传给视图,运输工具使用commandObject。下面的代码展示了如何使用Portlet API获取数据并返回给视图层。

@Override 
protected ModelAndView handleRenderRequestInternal(  
   RenderRequest request, RenderResponse response) throws Exception   
{        
   logger.info ("Inside Controller handleRenderRequestInternal");        
   Map< String, CommandObject> model = new   
     HashMap< String, CommandObject>();     
   CommandObject commandObject =   
     (CommandObject)request.getPortletSession().getAttribute(  
     CommandObject.COMMAND_NAME,PortletSession.APPLICATION_SCOPE);  
   if (commandObject == null){  
      commandObject = new CommandObject();  
   }  
              
   // logic to get the data and put it in the commandObject   
   // should be here...  
              
   String view = getFormView();  
   model.put("commandObject", commandObject);  
   ModelAndView mav = new ModelAndView(view, model);  
   return mav;    
}  
@Override 
public void onSubmitAction (final ActionRequest request,   
  final ActionResponse response, final Object command,  
  final BindException bindException) throws Exception   
{  
   logger.info ("Inside onSubmitAction");  
   // Set the form bean into session so that it will be available   
   CommandObject commandObject = (CommandObject)command;  
   logger.info("Command Object :"+ToStringBuilder.reflectionToString(  
      commandObject));  
   request.getPortletSession ().setAttribute ("command_obj",   
      command,PortletSession.APPLICATION_SCOPE);  
}  
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

在JSP文件中,你可以象下面这样检索数据:

< form:form action="${formAction}" name="quickProcess"   
   method="post" commandName="commandObject">      
  < form:hidden path="p" id="p" /> 
  < c:if test="${commandObject.someList != null}"> 
    < c:forEach items="${commandObject.someList}"   
      var="listItem" varStatus="loop">                
      < c:out value="${listItem.name}"/>< br>              
    < /c:forEach> 
  < /c:if> 
< /form:form> 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

注意这个Portlet并没有指出它在屏幕上的布局,是否可以调整大小,宽度和高度应该保持多少为佳,这些属性都由Portlet容器来进行控制的。

为了让Portlet可以真正运行,你还需要编译并部署它。在编译时,创建一个标准的Java war文件(一般使用Ant或Maven创建),部署时将war文件放到托管Portlet容器的应用服务器上。当Portlet配置好,且在Portlet容器中注册后,就要借助portlet.xml文件查找哪些容器中可以使用哪些Portlet了。例如,在Vignette Portal中,你可以通过搜索找到需要的Portlet,然后将其添加到门户中,如图5和图6所示。

在Vignette中添加一个Portlet 
图 5 在Vignette中添加一个Portlet

在Vignette中搜索Portlet 
图 6 在Vignette中搜索Portlet

添加Portlet到Portlet容器后,你还可以设置它们的位置、布局和属性,例如,你可以设置默认的宽度和位置,以及是否可以最小化和移动位置等。

图7显示了Vignette示例页面有三个Portlet,当用户登录到门户后默认就看到这三个Portlet。

在Vignette调整Portlet布局 
图 7 在Vignette调整Portlet布局

图8显示了eXo JBoss Portlet容器默认的布局,当然你也可以在此基础上重新调整,以符合你特殊需要。

eXo JBoss 中可选的Portlet容器默认布局 
图 8  eXo JBoss 中可选的Portlet容器默认布局

通过Portlet容器可以很容易地改变整个网站的外观,风格,只需要改变Portlet的布局、皮肤或UI主题即可。

小结

本文介绍了门户和Portlet的入门基础知识,并提供了一个简单的实例,对如何创建和部署Portlet做了简要说明。目前既有开源的也有商业化的门户产品,不管采用哪种产品,基于门户的开发将使程序员的重心转移到业务逻辑上。门户技术还处于不断发展中,未来几年有可能出现新的门户技术,如果你正从事企业级开发,那么从现在开始关注门户技术吧!

原文:An Introduction to Java Enterprise Portals and Portlet Development

作者:Vlad Kofman

责任编辑:yangsai 来源: 51CTO.com
相关推荐

2011-12-20 09:24:15

2012-02-13 10:07:52

Linux服务器

2012-02-13 09:52:00

Linux企业

2009-08-25 13:48:01

Java EE架构企业级应用

2009-11-23 20:10:31

ibmdwPortlet

2025-01-10 09:47:43

blockSDKiOS

2011-11-15 19:14:05

SAPNetWeaverOpenText

2012-05-07 09:45:46

VMware

2011-12-20 11:05:29

JBoss企业门户红帽

2011-12-13 10:06:11

2011-03-10 09:07:47

liferayportlet

2010-08-09 08:48:46

File APIWeb

2012-05-04 10:16:51

vmware虚拟化View VDI

2009-06-17 10:56:19

JBoss门户Enterprise门

2010-08-27 10:41:41

iPhone核心应用程序

2010-02-26 17:54:54

python

2009-11-06 16:10:54

ClosureJavaScript开Google

2015-03-31 15:33:57

开放实验室探秘企业ICT解决方案华为

2011-06-13 10:54:20

JAVA

2013-04-17 10:06:55

Google GlasMirror API
点赞
收藏

51CTO技术栈公众号