`
billy_zh
  • 浏览: 61726 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Liferay中外部war方式整合portlet的处理流程

阅读更多

  本文介绍如何以外部war应用的方式向liferay portal集成portlet, 还是以sample-jsp-portlet为例来说明。

一,portlet如何被liferay portal调用?

在web-xml定义中,可以看到这样的一个servlet定义:  

 〈servlet〉
  〈servlet-name〉sample_jsp_portlet〈/servlet-name〉
  〈servlet-class〉com.liferay.portal.kernel.servlet.PortletServlet〈/servlet-class〉
  〈init-param〉
   〈param-name〉portlet-class〈/param-name〉
   〈param-value〉com.sample.jsp.portlet.JSPPortlet〈/param-value〉
  〈/init-param〉
  〈load-on-startup〉0〈/load-on-startup〉
 〈/servlet〉
 〈servlet-mapping〉
  〈servlet-name〉sample_jsp_portlet〈/servlet-name〉
  〈url-pattern〉/sample_jsp_portlet/*〈/url-pattern〉
 〈/servlet-mapping〉

在[Liferay中action的处理流程]一文中,分析了当portlet是一个war时,liferay portal将转发请求到war应用,代码如下:
  CachePortlet
    private void _invoke(
   PortletRequest req, PortletResponse res, boolean action)
 throws IOException, PortletException {

 if (_portletConfig.isWARFile()) {
  String path =
   StringPool.SLASH + _portletConfig.getPortletName() + "/invoke";
  RequestDispatcher rd =
   _portletCtx.getServletContext().getRequestDispatcher(path);

  // 一些初始化代码....
  httpReq.setAttribute(WebKeys.JAVAX_PORTLET_PORTLET, _portlet);
  rd.include(httpReq, httpRes);
 }
 else {
  // 略...
 }
  }
  在本文中portletName为sample_jsp_portlet,则path为/sample_jsp_portlet/invoke,这个url对应的servlet就是PortletServlet了,
  这样liferay portlet就将请求转发到了sample-jsp-portlet应用。
  这里要注意的是servlet-name与url-pattern的名称一定要保持一致!

// PortletServlet
 public class PortletServlet extends HttpServlet {
 public static final String JAVAX_PORTLET_PORTLET = "javax.portlet.portlet";
 public static final String JAVAX_PORTLET_REQUEST = "javax.portlet.request";
 public static final String JAVAX_PORTLET_RESPONSE = "javax.portlet.response";
 public static final String PORTLET_CLASS_LOADER = "PORTLET_CLASS_LOADER";

 public void service(HttpServletRequest req, HttpServletResponse res)
  throws IOException, ServletException {

  PortletRequest portletReq =
   (PortletRequest)req.getAttribute(JAVAX_PORTLET_REQUEST);
  PortletResponse portletRes =
   (PortletResponse)req.getAttribute(JAVAX_PORTLET_RESPONSE);
  Portlet portlet = (Portlet)req.getAttribute(JAVAX_PORTLET_PORTLET);

  LiferayPortletSession portletSes =
   (LiferayPortletSession)portletReq.getPortletSession();
  portletSes.setHttpSession(req.getSession());

  if (portletReq instanceof ActionRequest) {
   ActionRequest actionReq = (ActionRequest)portletReq;
   ActionResponse actionRes = (ActionResponse)portletRes;

   portlet.processAction(actionReq, actionRes);
  }
  else {
   RenderRequest renderReq = (RenderRequest)portletReq;
   RenderResponse renderRes = (RenderResponse)portletRes;

   portlet.render(renderReq, renderRes);
  }
 }
 }
PortletServlet很简单,它根据PortletRequest的类型来决定是让portlet执行processAction还是render,
portlet由servlet定义的init-param参数指定,它必须继承自javax.portlet.GenericPortlet类, 这是jsr168规范所指定的,
本文中为com.sample.jsp.portlet.JSPPortlet。

// JSPPortlet.
 public class JSPPortlet extends GenericPortlet {

 public void init() throws PortletException {
  editJSP = getInitParameter("edit-jsp");
  helpJSP = getInitParameter("help-jsp");
  viewJSP = getInitParameter("view-jsp");
 }

 public void doDispatch(RenderRequest req, RenderResponse res)
  throws IOException, PortletException {
  // 略...
 }

 public void doEdit(RenderRequest req, RenderResponse res)
  throws IOException, PortletException {
  // 略...
 }

 public void doHelp(RenderRequest req, RenderResponse res)
  throws IOException, PortletException {
  include(helpJSP, req, res);
 }

 public void doView(RenderRequest req, RenderResponse res)
  throws IOException, PortletException {
  include(viewJSP, req, res);
 }

 public void processAction(ActionRequest req, ActionResponse res)
  throws IOException, PortletException {
 }

 protected void include(String path, RenderRequest req, RenderResponse res)
  throws IOException, PortletException {
  PortletRequestDispatcher prd =
   getPortletContext().getRequestDispatcher(path);

  if (prd == null) {
   _log.error(path + " is not a valid include");
  }
  else {
   prd.include(req, res);
  }
 }
 }
这里一个很简单的portlet实现,它直接包含jsp文件。

二,portlet中的预定义对象.

portlet 页面上能使用的对象.
首先在portlet页面上加入如下tag定义,
<portlet:defineobjects></portlet:defineobjects>

 DefineObjectsTag.doStartTag() {
 ClassLoader contextClassLoader =
  Thread.currentThread().getContextClassLoader();

 try {
  // 切换ClassLoader,对于采用War方式的portlet,这是必须的?
  Thread.currentThread().setContextClassLoader(
   PortalClassLoaderUtil.getClassLoader());

  MethodWrapper methodWrapper = new MethodWrapper(
   _TAG_CLASS, _TAG_DO_START_METHOD, new Object[] {pageContext});

  MethodInvoker.invoke(methodWrapper);
 }
 catch (Exception e) {
  throw new JspException(e);
 }
 finally {
  Thread.currentThread().setContextClassLoader(contextClassLoader);
 }

 return SKIP_BODY; 
 }

 private static final String _TAG_CLASS =
  "com.liferay.portlet.DefineObjectsTagUtil";
 private static final String _TAG_DO_START_METHOD = "doStartTag";


 DefineObjectsTag.doStartTag(PageContext pageContext) {
 ServletRequest req = pageContext.getRequest();

 PortletConfigImpl portletConfig =
  (PortletConfigImpl)req.getAttribute(WebKeys.JAVAX_PORTLET_CONFIG);

 if (portletConfig != null) {
  pageContext.setAttribute("portletConfig", portletConfig);
  pageContext.setAttribute(
   "portletName", portletConfig.getPortletName());
 }

 RenderRequest renderRequest =
  (RenderRequest)req.getAttribute(WebKeys.JAVAX_PORTLET_REQUEST);

 if (renderRequest != null) {
  pageContext.setAttribute("renderRequest", renderRequest);
  pageContext.setAttribute(
   "portletPreferences", renderRequest.getPreferences());
  pageContext.setAttribute(
   "portletSession", renderRequest.getPortletSession());
 }

 RenderResponse renderResponse =
  (RenderResponse)req.getAttribute(WebKeys.JAVAX_PORTLET_RESPONSE);

 if (renderResponse != null) {
  pageContext.setAttribute("renderResponse", renderResponse);
 }

 if (portletConfig.isWARFile() && ServerDetector.isWebLogic()) {
  PortletSessionImpl portletSession =
   (PortletSessionImpl)renderRequest.getPortletSession();

  PortletSessionPool.put(
   portletSession.getPortalSessionId(), pageContext.getSession());
 }
 }

从上面的代码可以看出,在portlet的页面上可以直接使用的对象有:
portletConfig  // portlet配置.
portletName   // portlet名称.
renderRequest  // portlet request.
portletPreferences // portlet 首选项.
portletSession // portlet 会话.
renderResponse // portlet response.


三,portlets如何部署到liferay portal中?

在portlets应用的web.xml中加入如下listener定义:
  <listener></listener>
     <listener-class></listener-class> com.liferay.portal.kernel.servlet.PortletContextListener
 

加入了此listener后,当portlets应用启动时,将会向liferay portal注册此应用中的所有定义的portlet;
当portlets应用停止时,会向liferay portal取消所有定义的portlet,  portlet定义当然是由portlet.xml文件给出。

 PortletContextListener实现了ServletContextListener接口.

   public class PortletContextListener implements ServletContextListener {
 public void contextInitialized(ServletContextEvent sce) {
  HotDeployUtil.fireDeployEvent(
   new HotDeployEvent(
    sce.getServletContext(),
    Thread.currentThread().getContextClassLoader()));
 }

 public void contextDestroyed(ServletContextEvent sce) {
  HotDeployUtil.fireUndeployEvent(
   new HotDeployEvent(
    sce.getServletContext(),
    Thread.currentThread().getContextClassLoader()));
 }
  }
  上面的两个方法直接调用HotDeployUtil的fireDeployEvent和fireUndeployEvent方法,HotDeployUtil是Liferay portal的热部署工具类,

  下面来着重看看fireDeployEvent是如何执行portlet注册的,

  public class HotDeployUtil {

 // 构造一个实例,singlton模式.
 private static HotDeployUtil _instance = new HotDeployUtil();

 // 触发部署事件,通知HotDeployListener进行部署。
 private synchronized void _fireDeployEvent(HotDeployEvent event) {
 
             // 当_events不为空时,说明liferay portal还没启动,此时只是简单的保存event.
      if (_events != null) {              
  _events.add(event);
  return;
      }

       Iterator itr = _listeners.iterator();

       // 遍历HotDeployListener,通知它们进行部署.
      while (itr.hasNext()) {
  HotDeployListener listener = (HotDeployListener)itr.next();

  try {
     listener.invokeDeploy(event);
  }  catch (HotDeployException hde) {
     _log.error(StackTraceUtil.getStackTrace(hde));
  }
     }
 }

        // 在liferay portal启动后被调用,此设计的目的主要用于解决portals应用在liferay portal应用之前启动的情况。
 private synchronized void _flushEvents() {
     for (int i = 0; i < _events.size(); i++) {
      HotDeployEvent event = (HotDeployEvent)_events.get(i);

       Iterator itr = _listeners.iterator();

          while (itr.hasNext()) {
  HotDeployListener listener = (HotDeployListener)itr.next();

  try {
     listener.invokeDeploy(event);
  }
  catch (HotDeployException hde) {
     _log.error(StackTraceUtil.getStackTrace(hde));
  }
      }
    }
    _events = null;
 }

 // 注册HotDeployListener
 private void _registerListener(HotDeployListener listener) {
  _listeners.add(listener);
 }

 private List _listeners;
 private List _events;
  }
  这里就有个问题了,HotDeployListener是在哪里加入进来的了?答案是在liferay portal的初始化时加入的。

// liferay portal初始化
  public class MainServlet extends ActionServlet {
      public void init() {        
          // 其它初始化处理......
          try {
                // 执行全局启动事件.
  EventsProcessor.process(PropsUtil.getArray(
   PropsUtil.GLOBAL_STARTUP_EVENTS), true);
  // 执行应用程序启动事件.
  EventsProcessor.process(PropsUtil.getArray(
   PropsUtil.APPLICATION_STARTUP_EVENTS),
     new String[] {_companyId});
  }
  catch (Exception e) {
  _log.error(e);
  }
      PortalInstances.init(_companyId);
      } 
   }
   EventsProcessor是liferay portal中的事件处理器,liferay portal中定义了一系列的事件点,
   像上面的APPLICATION_STARTUP_EVENTS,就是应用程序启动事件点.
   通过配置文件,就可以将我们的自定义事件加入到这些事件点里去执行了。
   关于EventProcessor更详细的内容,将用另外一篇文章来详细介绍,这里只需要知道有两个事件被触发了。

// 全局启动事件。
  public class GlobalStartupAction extends SimpleAction {

      public void run(String[] ids) throws ActionException {

      // JCR 处理...

      // Portal initable
      PortalInitableUtil.flushInitables();

      // Hot deploy
      // 注册layoutTemplate监听器.
      HotDeployUtil.registerListener(new HotDeployLayoutTemplateListener());
      // 注册portlet监听器.
      HotDeployUtil.registerListener(new HotDeployPortletListener());
      // 注册theme监听器.
      HotDeployUtil.registerListener(new HotDeployThemeListener());
      // 清理所有事件.
      HotDeployUtil.flushEvents();

      // Auto deploy 自动部署处理.....
  }

// 部署Portlets

 public class HotDeployPortletListener implements HotDeployListener {

 public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
  String servletContextName = null;

  try {
   // Servlet context
   ServletContext ctx = event.getServletContext();
   servletContextName = ctx.getServletContextName();

   // Company ids
   String[] companyIds = StringUtil.split(
    ctx.getInitParameter("company_id"));
   if ((companyIds.length == 1) && (companyIds[0].equals("*"))) {
    companyIds = PortalInstances.getCompanyIds();
   }

   // Initialize portlets, 初始化portlets
   String[] xmls = new String[] {
    Http.URLtoString(ctx.getResource("/WEB-INF/portlet.xml")),
    Http.URLtoString(ctx.getResource(
     "/WEB-INF/liferay-portlet.xml")),
    Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"))
   };
   List portlets = PortletLocalServiceUtil.initWAR(
    servletContextName, xmls);

   // Class loader, 类装载器
   ClassLoader portletClassLoader = event.getContextClassLoader();
   ctx.setAttribute(
    PortletServlet.PORTLET_CLASS_LOADER, portletClassLoader);

   // portlet context wrapper, portlet上下文包装器.

   boolean strutsBridges = false;

   Iterator itr1 = portlets.iterator();

   while (itr1.hasNext()) {
    Portlet portlet = (Portlet)itr1.next();

    // 构造portlet实例.
    Class portletClass = portletClassLoader.loadClass(
      portlet.getPortletClass());
    javax.portlet.Portlet portletInstance =
     (javax.portlet.Portlet)portletClass.newInstance();

    // 检查是否struts桥接portlet.
    if (ClassUtil.isSubclass(portletClass,
     StrutsPortlet.class.getName())) {
     strutsBridges = true;
    }

    // 构造lucene indexer实例.
    Indexer indexerInstance = null;
    if (Validator.isNotNull(portlet.getIndexerClass())) {
     indexerInstance = (Indexer)portletClassLoader.loadClass(
      portlet.getIndexerClass()).newInstance();
    }

    // 构造scheduler实例.
    Scheduler schedulerInstance = null;

    if (Validator.isNotNull(portlet.getSchedulerClass())) {
     schedulerInstance = (Scheduler)portletClassLoader.loadClass(
      portlet.getSchedulerClass()).newInstance();
    }

    // 验证preferences.
    PreferencesValidator prefsValidator = null;

    if (Validator.isNotNull(portlet.getPreferencesValidator())) {
     prefsValidator =
      (PreferencesValidator)portletClassLoader.loadClass(
       portlet.getPreferencesValidator()).newInstance();

     try {
      if (GetterUtil.getBoolean(PropsUtil.get(
        PropsUtil.PREFERENCE_VALIDATE_ON_STARTUP))) {

       prefsValidator.validate(
        PortletPreferencesSerializer.fromDefaultXML(
         portlet.getDefaultPreferences()));
      }
     }
     catch (Exception e1) {
      _log.warn(
       "Portlet with the name " + portlet.getPortletId() +
        " does not have valid default preferences");
     }
    }

    // 资源处理.
    Map resourceBundles = null;

    if (Validator.isNotNull(portlet.getResourceBundle())) {
     resourceBundles = CollectionFactory.getHashMap();

     Iterator itr2 = portlet.getSupportedLocales().iterator();

     while (itr2.hasNext()) {
      String supportedLocale = (String)itr2.next();

      Locale locale = new Locale(supportedLocale);

      try {
       ResourceBundle resourceBundle =
        ResourceBundle.getBundle(
         portlet.getResourceBundle(), locale,
         portletClassLoader);

       resourceBundles.put(
        locale.getLanguage(), resourceBundle);
      }
      catch (MissingResourceException mre) {
       _log.warn(mre.getMessage());
      }
     }
    }

    // 定制用户特性。
    Map customUserAttributes = CollectionFactory.getHashMap();

    Iterator itr2 =
     portlet.getCustomUserAttributes().entrySet().iterator();

    while (itr2.hasNext()) {
     Map.Entry entry = (Map.Entry)itr2.next();

     String attrCustomClass = (String)entry.getValue();

     customUserAttributes.put(
      attrCustomClass,
      portletClassLoader.loadClass(
       attrCustomClass).newInstance());
    }

    PortletContextWrapper pcw = new PortletContextWrapper(
     portlet.getPortletId(), ctx, portletInstance,
     indexerInstance, schedulerInstance, prefsValidator,
     resourceBundles, customUserAttributes);

    PortletContextPool.put(portlet.getPortletId(), pcw);
   }

   // Struts bridges

   if (strutsBridges) {
    ctx.setAttribute(
     ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER,
     new LiferayServletContextProvider());
   }

   // Portlet display,portlet展示处理,显示在portlet添加面板上位置.

   String xml = Http.URLtoString(ctx.getResource(
    "/WEB-INF/liferay-display.xml"));
   PortletCategory newPortletCategory =
    PortletLocalServiceUtil.getWARDisplay(servletContextName, xml);

   for (int i = 0; i < companyIds.length; i++) {
    String companyId = companyIds[i];

    PortletCategory portletCategory =
     (PortletCategory)WebAppPool.get(
      companyId, WebKeys.PORTLET_CATEGORY);

    if (portletCategory != null) {
     portletCategory.merge(newPortletCategory);
    }
    else {
     _log.error(
      "Unable to register portlet for company " + companyId +
       " because it does not exist");
    }
   }

   // Variables

   _vars.put(
    servletContextName, new ObjectValuePair(companyIds, portlets));

   if (_log.isInfoEnabled()) {
    _log.info(
     "Portlets for " + servletContextName +
      " registered successfully");
   }
  }
  catch (Exception e2) {
   throw new HotDeployException(
    "Error registering portlets for " + servletContextName, e2);
  }
 }
 }

关于layoutTemplates和theme的部署这里就不列出了,请自行参考源代码。

分享到:
评论
1 楼 Hojave 2010-11-15  
楼主,这种方式的中文资源应用应该怎么加呢?我在Portlet的配置文件中引入了资源文件,但实际运行的时候却无法读到呢。

相关推荐

    Liferay_Control-Panel_Portlet DTD-5.2_MVC-Portlet

    在Liferay中,MVC Portlet是最常见的portlet类型,它通过分离业务逻辑(Model)、用户界面(View)和控制流程(Controller)来实现代码的清晰组织。开发者可以使用Java Servlet和JSP来编写MVC Portlets,它们在...

    Liferay portlet 工程示例代码

    Liferay Portal是一款开源的企业级门户系统,它允许用户创建、管理和集成各种Web应用程序,而portlet就是这些应用的基本构建块。下面将详细阐述Liferay Portlet开发的相关知识点。 1. **Liferay Portal简介** ...

    Liferay-Portlet-SDK5.2.3.rar_liferay_liferay sdk_portlet

    Liferay Portlet SDK 5.2.3是该系列的一个特定版本,专为Liferay Portal 5.2.3版本设计,它包含了开发portlet所需的类库、文档和示例代码,使得开发者能够高效地集成自定义功能到Liferay Portal环境中。 **portlet...

    Liferay porta 清理不需要的Portlet 代码

    在Liferay Portal中,Portlet是可重用的Web组件,用于展示动态内容和服务。随着时间的推移,项目中可能会积累许多不再使用的Portlet代码,这不仅会占用服务器资源,还可能影响系统的性能和维护效率。因此,定期清理...

    liferay-spring-portlet

    通过深入学习和理解这个项目,开发者可以掌握如何在Liferay中创建和部署Spring Portlet,了解portlet的生命周期和交互方式,以及Spring框架在portlet开发中的应用。同时,如果进一步集成Hibernate,还可以学习到如何...

    liferay_使用struts2开发portlet

    通过上述步骤,我们可以了解到在Liferay环境中使用Struts2开发Portlet的具体流程。整个过程涉及了开发环境的搭建、项目创建、Portlet类的编写以及Struts配置文件的设置等多个环节。这些步骤不仅为初学者提供了清晰的...

    liferay一个简单的portlet

    Portlet是Liferay中的核心组件,是构建门户应用程序的基本模块。这篇博文将深入探讨如何在Liferay中创建一个简单的Portlet,帮助开发者更好地理解Liferay的开发过程。 首先,让我们了解Portlet的基本概念。Portlet...

    Liferay跨页面Portlet之间的调用与数据传递.docx

    在Liferay中,跨页面Portlet之间的调用与数据传递是一项关键任务,因为这涉及到不同Portlet间的协作和信息共享。Liferay提供了多种通信机制,包括PortletSession、Public Render Parameters以及Portlet事件,但这些...

    liferay portlet 开发实例

    在IT领域,特别是企业级应用开发中,Liferay作为一个强大的企业门户平台,提供了丰富的功能和服务,其中Liferay Portlet的开发是构建定制化企业应用的关键技术之一。本文将基于提供的文件信息,深入解析Liferay ...

    liferay 整合struts例子

    通过这个实例,开发者可以深入理解Liferay与Struts的集成方式,以及如何在实际项目中应用这种整合,提高开发效率和系统稳定性。对于初学者,这是一个很好的起点,可以从中学习到portlet开发、MVC设计模式以及企业级...

    liferay-spring-mvc-portlet:Liferay Spring MVC portlet 的项目模板

    Liferay Spring Portlet MVC 使用 Maven 的 Liferay Spring Portlet MVC 项目模板。 Liferay EE 6.2.10.11 (GA1, SP... $ cp target/liferay-spring-mvc-portlet.war $LIFERAY_HOME/deploy/ 配置 默认设置为 Liferay

    liferay portlet例子

    liferay portlet例子 liferay portlet例子

    portlet开发以及相应的工具包,如何与tomcat整合

    总的来说,portlet开发涉及了Java Web技术、portlet规范和特定的开发工具,而将portlet整合到Tomcat中则需要对服务器配置和部署流程有深入理解。通过学习和实践,开发者可以创建出高效、灵活的企业级portlet应用。

    liferay_portlet对象详解

    Portlet 技术的理解和应用是开发 Liferay 应用程序的关键。本文将深入解析 Portlet 的关键概念,包括 Portlet 请求与 URL、Portlet 模式和窗口状态。 1. Portlet 请求与 URL 在 Liferay 的 Portal 页面中,多个 ...

    CAS, Liferay和Alfresco portlet三者的整合

    4. **工作流程整合**:利用Alfresco的工作流程引擎,可以为Liferay中的portlet定义和执行复杂的业务流程,提高工作效率。 为了实现这样的整合,我们需要进行以下步骤: 1. **配置CAS服务器**:首先,要在CAS服务器...

    liferay portlet demo

    "Liferay Portlet Demo"是一个示例项目,旨在展示如何在Liferay环境中开发、部署和使用portlet。这个项目通常包含一系列的源代码、配置文件以及必要的资源,用于帮助开发者理解和实践portlet开发。 【描述】:...

    Liferay5.2.3 样例Portlet

    Liferay5.2.3的样例,通过war.bat打包后发布到相应的liferay环境即可。 另外,需要配置context,tomcat中的代码如下: &lt;/Context&gt;

    liferay portlet开发

    在Liferay中,Portlet是一种用于显示内容和服务的应用程序组件,它是门户应用程序的核心部分。本文档旨在详细介绍如何通过插件(plugin)方式开发Liferay Portlet。 #### 二、开发模式的选择 Liferay提供了两种主要...

    liferay-portlet-development

    《Liferay Portlet 开发——全面指南》:深入解析与实战技巧 ...通过理论结合实践的方式,本书将带领读者深入了解Liferay平台,掌握portlet开发的全过程,为成为优秀的Liferay开发者打下坚实的基础。

    liferay portlet

    liferay-display.xml文件用来定义portlet在Liferay控制台中的分类,以及在页面布局中的显示方式。这有助于用户管理和组织他们的portlet,同时控制用户的访问权限。 Liferay在其基础上扩展了Portlet模式,除了标准的...

Global site tag (gtag.js) - Google Analytics