经过同事的帮忙,加上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) && (layout.isTypeControlPanel())) {
String category = portlet.getControlPanelEntryCategory();
if (Validator.equals(category, "content")) {
layout = null;
boolean access = contains(
permissionChecker, scopeGroupId, layout, portlet, "VIEW");
if ((access) && (!PropsValues.TCK_URL) &&
(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()) && (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<String, Portlet> portletsPool,
Set<String> liferayPortletIds, Map<String, String> 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中加一个元素:
<system>true</system>
就可以了。
经过测试,当我们为portlet添加了这个元素之后,果然在Enterprise Edition上,这个portlet的resourceURL可以被正确的访问。
本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/905357,如需转载请自行联系原作者