天天看点

Liferay 6.1 从CE 迁移到 EE版本的一个棘手的问题(2)

经过同事的帮忙,加上4天的努力,这个问题现在终于水落石出了。

为什么这问题只在Liferay Enterprise Edition发生而在Community Edition上不发生?

因为,在访问这个Portlet之前会走一系列的拦截器:

对于Community Edition,拦截器的调用顺序为:

<a href="http://blog.51cto.com/attachment/201206/223559886.png" target="_blank"></a>

而对于Enterprise Edition,拦截器调用顺序为:

<a href="http://blog.51cto.com/attachment/201206/223645126.png" target="_blank"></a>

所以这里可以看出来,在执行完最后一个拦截器之后,在CE版本,成功的把请求发给了portlet,而在EE版本,请求没有发送给portlet.

我们断点跟进,终于发现了这2个的区别:

在CE版本,当最终调用到LayoutAction类的processPortletRequest方法时,其代码如下(省略很多不重要的代码):

protected Portlet processPortletRequest(HttpServletRequest request, HttpServletResponse response, String lifecycle) 

    throws Exception 

  { 

    HttpSession session = request.getSession(); 

    .... 

    if (lifecycle.equals("RESOURCE_PHASE")) { 

      PortletDisplay portletDisplay = themeDisplay.getPortletDisplay(); 

      String portletPrimaryKey = PortletPermissionUtil.getPrimaryKey( 

        layout.getPlid(), portletId); 

      portletDisplay.setId(portletId); 

      portletDisplay.setRootPortletId(portlet.getRootPortletId()); 

      portletDisplay.setInstanceId(portlet.getInstanceId()); 

      portletDisplay.setResourcePK(portletPrimaryKey); 

      portletDisplay.setPortletName(portletConfig.getPortletName()); 

      portletDisplay.setNamespace( 

        PortalUtil.getPortletNamespace(portletId)); 

      WebDAVStorage webDAVStorage = portlet.getWebDAVStorageInstance(); 

      if (webDAVStorage != null) { 

        portletDisplay.setWebDAVEnabled(true); 

      } 

      else { 

        portletDisplay.setWebDAVEnabled(false); 

      ResourceRequestImpl resourceRequestImpl =  

        ResourceRequestFactory.create( 

        request, portlet, invokerPortlet, portletContext,  

        windowState, portletMode, portletPreferences,  

        layout.getPlid()); 

      ResourceResponseImpl resourceResponseImpl =  

        ResourceResponseFactory.create( 

        resourceRequestImpl, response, portletId, companyId); 

      resourceRequestImpl.defineObjects( 

        portletConfig, resourceResponseImpl); 

      try 

      { 

        ServiceContext serviceContext =  

          ServiceContextFactory.getInstance(resourceRequestImpl); 

        ServiceContextThreadLocal.pushServiceContext(serviceContext); 

        invokerPortlet.serveResource( 

          resourceRequestImpl, resourceResponseImpl); 

      finally { 

        ServiceContextThreadLocal.popServiceContext(); 

    } 

    return portlet; 

  } 

而在Enterprise Edition版本,同类名的方法如下:

        boolean access = PortletPermissionUtil.hasAccessPermission( 

          permissionChecker, scopeGroupId, layout, portlet,  

          portletMode); 

        if (!access) break label947; 

        label947: invokerPortlet.serveResource( 

      finally 

所以,我们对比CE的50-51行和EE的第50-57行可以发现:在CE版本,它总是把请求交给serveResource()方法,而EE版本,则会有一个boolean变量access,要这个变量的值为true时才会吧请求交给serveResource()方法,这就是为什么在CE版本下,任何用户,包括未登录用户都可以访问最终资源,而在EE版本,只有注册用户才可以访问。因为Guest用户在执行这段代码时候access布尔值总为false.

解决方案:

那么我们如何在EE版本中解决这个问题呢?

很简单,我们只要设法让这个access值永远为true就可以确保在EE版本上,就算是Guest用户也能访问这个资源(JSON资源)

为此,我们继续看这段access值的获取:

boolean access = PortletPermissionUtil.hasAccessPermission( 

它会去调用下面的代码:

public boolean hasAccessPermission(PermissionChecker permissionChecker, long scopeGroupId, Layout layout, Portlet portlet, PortletMode portletMode) 

    throws PortalException, SystemException 

    if ((layout != null) &amp;&amp; (layout.isTypeControlPanel())) { 

      String category = portlet.getControlPanelEntryCategory(); 

      if (Validator.equals(category, "content")) { 

        layout = null; 

    boolean access = contains( 

      permissionChecker, scopeGroupId, layout, portlet, "VIEW"); 

    if ((access) &amp;&amp; (!PropsValues.TCK_URL) &amp;&amp;  

      (portletMode.equals(PortletMode.EDIT))) 

    { 

      access = contains( 

        permissionChecker, scopeGroupId, layout, portlet,  

        "PREFERENCES"); 

    return access; 

而这段代码中的又多了一个布尔字段叫access,它是通过以下代码获得的:

public boolean contains(PermissionChecker permissionChecker, long groupId, Layout layout, Portlet portlet, String actionId, boolean strict) 

    if (portlet.isUndeployedPortlet()) { 

      return false; 

    if ((portlet.isSystem()) &amp;&amp; (actionId.equals("VIEW"))) { 

      return true; 

    return contains( 

      permissionChecker, groupId, layout, portlet.getPortletId(),  

      actionId, strict); 

从08行我们就清楚了,只要我们吧这个portlet设为isSystem()返回true就行了,那么我们如何做到这点呢?

继续跟踪我们可以看到这个isSystem()实际是由下面这段代码设置的:

(在PortletLocalServiceImpl的_readLiferayPortletXML方法中)见第06-08行:

private void _readLiferayPortletXML( 

        String servletContextName, Map&lt;String, Portlet&gt; portletsPool, 

        Set&lt;String&gt; liferayPortletIds, Map&lt;String, String&gt; roleMappers, 

        Element portletElement) { 

.. 

    portletModel.setSystem( 

            GetterUtil.getBoolean( 

                portletElement.elementText("system"), portletModel.isSystem())); 

portletModel.setActive( 

                portletElement.elementText("active"), portletModel.isActive())); 

        portletModel.setInclude( 

            GetterUtil.getBoolean(portletElement.elementText("include"), 

            portletModel.isInclude())); 

因为这段代码是读取portlet的liferay-portlet.xml配置文件的,所以我们需要做的只是在portlet中加一个元素:

&lt;system&gt;true&lt;/system&gt; 

就可以了。

经过测试,当我们为portlet添加了这个元素之后,果然在Enterprise Edition上,这个portlet的resourceURL可以被正确的访问。

本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/905357,如需转载请自行联系原作者