深入浅出Liferay Portal (4)
来源:互联网 发布:mac用什么免费杀毒软件 编辑:程序博客网 时间:2024/06/06 13:21
上一篇讲到当用户在浏览器地址栏上输入http://localhost:8080/后,经过几次迭代,服务器最终处理的是http://localhost:8080/c/portal/update,那当服务器收到/c/portal/update的请求后做了什么动作,最后是怎么向浏览器发送网页信息呢?
1. 首先MainServlet接收这个请求,然后传递到LayoutAction。(具体怎么传递后续会介绍到)
在struts-config.xml中有定义,表明有关"/portal/layout"的请求是由LayoutAction来处理的。而在tiles-def.xml中定义"/portal/layout"最终传递到"/portal/layout.jsp"。
struts-config.xml
------------------------
<action path="/portal/layout" type="com.liferay.portal.action.LayoutAction">
<forward name="portal.layout" path="portal.layout" />
</action>
tiles-def.xml
------------------
<definition name="portal.layout" extends="portal">
<put name="content" value="/portal/layout.jsp" />
<put name="selectable" value="true" />
</definition>
2. 下面分析LayoutAction的简要流程
LayoutAction.execute()
--------------------------------
ThemeDisplay themeDisplay = (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
Layout layout = themeDisplay.getLayout();
/* 加入/html/portal/layout/view/portlet.jsp 到网页*/
incluldeLayoutContent(req, res, themeDisplay, layout);
/* 加入/html/portal/layout.jsp 到网页,先portlet.jsp,后layout.jsp*/
return mapping.findForward("portal.layout");
LayoutAction.includeLayoutContent()
-----------------------------------------------------
String path = StrutsUtil.TEXT_HTML_DIR;
/* path = "/html" */
path += PortalUtil.getLayoutViewPage(layout);
/* path = "/html/portal/layout/view/portlet.jsp" */
RequestDispatcher rd = ctx.getRequestDispatcher(path);
rd.include(req, stringServletRes);
可见两个jsp文件是页面的关键所在。
/portal/layout/view/portlet.jsp
-----------------------------------------
<%
String content = LayoutTemplateLocalUtil.getContent(layoutTypePortlet.getLayoutTemplateId(),
false, theme.getThemeId());
%>
<%= RuntimePortletUtil.processTemplate(application, request, response, pageContext, content) %>
/*****************************************************
其中content的内容通过断点调试可以知道(是什么时候生成的呢?)
<div class="columns-2" id="content-wrapper">
<table id="layout-grid">
<tr>
<td class="lfr-column thirty" id="column-1" valign="top">
$processor.processColumn("column-1")
</td>
<td class="lfr-column seventy" id="column-2" valign="top">
$processor.processColumn("column-2")
</td>
</tr>
</table>
</div>
*****************************************************/
/* RuntimePortletUtil.processTemplate()函数返回的是一个很长很长的字符串,就是最终用于显示页面上各个portlet的代码,下面有分析 */
RuntimePortletUtil.processTemplate()
------------------------------------------------------
TemplateProcessor processor = new TemplateProcessor(ctx, req, res, portletId);
VelocityContext vc = new VelocityContext();
vc.put("processor", processor);
// Velocity variables
VelocityVariables.insertVariables(vc, req);
vc.put("taglibLiferay", velocityTaglib);
vc.put("theme", velocityTaglib);
StringWriter sw = new StringWriter();
Velocity.evaluate(vc, sw, RuntimePortletUtil.class.getName(), content);
String output = sw.toString();
/*****************************************************
output的初始内容为:
<div class="columns-3" id="content-wrapper">
<table id="layout-grid">
<tr>
<td class="lfr-column thirty" id="column-1" valign="top">
[$TEMPLATE_COLUMN_column-1$]
</td>
<td class="lfr-column thirty" id="column-2" valign="top">
[$TEMPLATE_COLUMN_column-2$]
</td>
<td class="lfr-column thirty" id="column-3" valign="top">
[$TEMPLATE_COLUMN_column-3$]
</td>
</tr>
</table>
</div>
*****************************************************/
Map columnsMap = processor.getColumnsMap();
while (itr.hasNext()) {
Map.Entry entry = (Map.Entry)itr.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
output = StringUtil.replace(output, key, value);
}
/*****************************************************
如果首页面上有四个portlet,名字为A,B,C,D,其中A在左边一列上,B,C同在中间一列,D在右边一列。则output的内容为:
<div class="columns-3" id="content-wrapper">
<table id="layout-grid">
<tr>
<td class="lfr-column thirty" id="column-1" valign="top">
<div class="lfr-portlet-column" id="layout-column_column-1">
[$TEMPLATE_PORTLET_A$]
</div>
</td>
<td class="lfr-column thirty" id="column-2" valign="top">
<div class="lfr-portlet-column" id="layout-column_column-2">
[$TEMPLATE_PORTLET_B$]
[$TEMPLATE_PORTLET_C$]
</div>
</td>
<td class="lfr-column thirty" id="column-3" valign="top">
<div class="lfr-portlet-column" id="layout-column_column-3">
[$TEMPLATE_PORTLET_D$]
</div>
</td>
</tr>
</table>
</div>
*****************************************************/
Map portletsMap = processor.getPortletsMap();
itr = portletsMap.entrySet().iterator();
while (itr.hasNext()) {
StringMaker sm = new StringMaker();
processPortlet(sm, ctx, req, res, portlet, queryString, columnId, columnPos,columnCount, null);
output = StringUtil.replace(output, "[$TEMPLATE_PORTLET_" + portlet.getPortletId() + "$]", sm.toString());
/*****************************************************
这里每迭代一个portlet,output的内容中就添加了该portlet的view.jsp代码段落,而且还会增加portlet的标准头部和边框,如最小化,关闭等按钮及其对应的JavaScript代码。
*****************************************************/
}
return output;
/* 这个output就是在portlet.jsp中要显示的内容 */
portal.jsp
------------
<c:choose>
<c:when test="<%= themeDisplay.isStateExclusive() %>">
<%= request.getAttribute(WebKeys.LAYOUT_CONTENT) %>
</c:when>
<c:when test="<%= themeDisplay.isStatePopUp() %>">
<liferay-theme:include page="portal_pop_up.jsp" />
</c:when>
<c:otherwise>
<liferay-theme:include page="portal_normal.jsp" />
</c:otherwise>
</c:choose>
在Liferay中没有发现portal_normal.jsp的踪影,发现portal_normal.vm似乎有点相关。portal_normal粉墨登场。
portal_normal.vm
-------------------------
#parse ($init)
<html dir="#language ("lang.dir")">
<head>
<title>$company_name - $the_title</title>
$theme.include($top_head_include)
#css ($css_main_file)
#js ($js_main_file)
#if ($company_logo != "")
<style type="text/css">
#banner .logo {background: url($company_logo) no-repeat;
height: ${company_logo_height}px;
width: ${company_logo_width}px; }
</style>
#end
</head>
<body class="$css_class">
$theme.include($top_messages_include)
<div id="wrapper">
<div id="banner">
<div id="banner-inner">
<h1 class="logo">
<a href="$company_url">$company_name</a>
</h1>
<div id="page-search">
$theme.journalContentSearch()
</div>
#parse ("$full_templates_path/dock.vm")
#if ($update_available_url)
<div class="popup-alert-notice">
<a class="update-available" href="$update_available_url">#language("updates-are-available-for-liferay")</a>
</div>
#end
#if ($has_navigation)
#parse ("$full_templates_path/navigation.vm")
#end
</div>
</div>
#if ($selectable)
$theme.include($content_include)
#else
<div id="content-wrapper" class="login">
$portletDisplay.recycle()
$portletDisplay.setTitle($the_title)
$theme.wrapPortlet("portlet.vm", $content_include)
</div>
#end
<div id="footer">
<p class="language">$theme.language()</p>
</div>
</div>
$theme.include($bottom_ext_include)
$theme.include($session_timeout_include)
$theme.include($sound_alerts_include)
</body>
</html>
可见关键是一些Velocity变量的值,例如$top_head_include。
init.vm
----------
#set ($bottom_ext_include = "$dir_include/common/themes/bottom.jsp")
#set ($content_include = "$dir_include$tilesContent")
#set ($session_timeout_include = "$dir_include/common/themes/session_timeout.jsp")
#set ($sound_alerts_include = "$dir_include/common/themes/sound_alerts.jsp")
#set ($top_head_include = "$dir_include/common/themes/top_head.jsp")
#set ($top_messages_include = "$dir_include/common/themes/top_messages.jsp")
可见$top_head_include就是top_head.jsp。
top_head.jsp
-------------------
<%@ include file="/html/common/themes/top_js.jspf" %>
<%@ include file="/html/common/themes/top_js-ext.jsp" %>
portal/portal-web/docroot/html/common/themes/top_js.jspf
<%
String[] javaScriptFiles = PropsUtil.getArray(PropsUtil.JAVASCRIPT_FILES);
for (int i = 0; i < javaScriptFiles.length; i++) {
%>
<script src="<%= themeDisplay.getPathJavaScript() %>/<%= javaScriptFiles[i] %>" type="text/javascript"></script>
<%
}
%>
那javascriptFiles又如何得到的呢?
portal.properties
------------------------
javascript.files=/
jquery/jquery.js,/
jquery/cookie.js,/
jquery/tabs.js,/
/
liferay/liferay.js,/
liferay/browser.js,/
liferay/util.js,/
liferay/language.js,/
liferay/layout.js,/
/
liferay/ajax.js,/
liferay/animate.js,/
liferay/auto_fields.js,/
liferay/color_picker.js,/
liferay/columns.js,/
liferay/dock.js,/
liferay/dynamic_select.js,/
liferay/freeform.js,/
liferay/layout_configuration.js,......
另外还有一个很重要的properties文件值得关注:language.properties。好累啊,还有很多细节可以继续挖掘,还涉及到Velocity的一些变量的操作,休息下先。
先前的文章提到/c/*的请求都是交给MainServlet处理的,现在分析对于不同的/c/*,例如/c/layout或者/c/update_layout等请求是怎么传递到不同的action去的呢。
下面以显示 Add Content 为例进行分析,Add Content 窗口其实是一个在 portlet-custom.xml 中定义的名为87的portlet。点击Add Content链接会产生一个Reqest,开始整个处理的流程。
1. Request的生成。
当时页面的源文件中有关的代码为:
<li class="add-content">
<a href="javascript: void(0);" onclick="LayoutConfiguration.toggle('10102', '87', '');">Add Content</a>
</li>
点击add content调用的是LayoutConfiguration.toogle()。
layout_configuratioin.js
var LayoutConfiguration = {
toggle : function (plid, ppid, doAsUserId) {
var url = themeDisplay.getPathMain() + "/portal/render_portlet?p_l_id=" + plid + "&p_p_id=" +ppid + "&doAsUserId=" + doAsUserId + "&p_p_state=exclusive";
AjaxUtil.update(url, popup, {onComplete: function(){
LayoutConfiguration.init();
Liferay.Util.addInputType();
Liferay.Util.addInputFocus();}
});};
/*总之是会生成一个Ajax请求发送到服务器,过程以后可以分析 */
用Wireshark捕捉到的Request数据如下,知道请求中含有/c/portal/render_portlet字段,那自然会送入 MainServlet处理了。
2. MainServlet.service()
/* 这里是Request首先到达的地方*/
ServletContext ctx = getServletContext();
req.setAttribute(WebKeys.CTX, ctx);
PortletRequestProcessor portletReqProcessor = PortletRequestProcessor.getInstance(this, moduleConfig);
ctx.setAttribute(WebKeys.PORTLET_STRUTS_PROCESSOR, portletReqProcessor);
callParentService(req, res);
/* 调用ActionServlet.service(),由于在ActionServlet中没有覆盖service方法,所以 MainServlet 的super.service() 追溯到HttpServlet 的service方法 */
3. HttpServlet.service()
if (method.equals(METHOD_GET)) { ,,,
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
}
/* 在 service 中,根据http的类型,分别调用 doPost, doGet, doPut, ...,由于ActionServlet中实现了doPost等方法,所以MainServlet.service()方法会传递到 ActionServlet中的doPost等方法。*/
4. ActionServlet.doPost()
process(request, response);
/* 在另外ActionServlet的不同方法中,例如doGet(),doPut(),都调用了process()方法。*/
5. ActionServlet.process()
ModuleConfig config = getModuleConfig(request);
RequestProcessor processor = getProcessorForModule(config);
if (processor == null) {
processor = getRequestProcessor(config);
}
processor.process(request, response);
/* 最终传递到对应的Request Processor 的process()方法中去,也就是PortalRequestProcessor.process() */
6. PortalRequestProcessor.process()
public void process(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
String path = super.processPath(req, res); //path = “/portal/render_portlet”
ActionMapping mapping = (ActionMapping)moduleConfig.findActionconfig(path);
super.process(req, res);
/*由于TilesRequestProcessor中没有定义process()方法,所以调用 RequestProcessor.process() */
}
7. RequestProcessor.process()
public void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String path = processPath(request, response);
//这里path = “/portal/render_portlet”.
ActionMapping mapping = processMapping(request, response, path);
//这里找到在struts-config.xml中定义的关于上面path的mapping.
Action action = processActionCreate(request, response, mapping);
//这里生成了RenderPortletAction.
ActionForward forward = processActionPerform(request, response, action, form, mapping);
processForwardConfig(request, response, forward);
}
至此,每个由 MainServlet 传递来的 Request 都能到达对应的 Action,例如 LayoutAction,RenderPortletAction,UpdateLayoutAction,由这些 Action 最终完成 Layout 和 Portlet 的呈现。这里对应的action是RenderPortletAction(由下面的struts-config.xml中定义)。
下面以RenderPortletAction为例说明进一步的action过程。在struts-config.xml这个文件中定义了action class, forward path。
<action path="/portal/layout" type="com.liferay.portal.action.LayoutAction">
<forward name="portal.layout" path="portal.layout" />
</action>
<action path="/portal/update_layout" type="com.liferay.portal.action.UpdateLayoutAction" />
<action path=“/portal/render_portlet” type=“com.liferay.portal.action.RenderPortletAction”>
<forward name=“/portal/render_portlet” path=“/portal/render_portlet.jsp”/>
</action>
<!-- 这里定义了由RenderPortletAction来处理”/portal/render_portlet”,而且最后要forward到” /portal/render_portlet.jsp”。所以最后会把render_portlet.jsp包含到response的页面当中-- >
那render_portlet.jsp又是如何实现最终把add content这个portlet显示出来的呢,下回接着分析。
- 深入浅出Liferay Portal (4)
- 深入浅出Liferay Portal (4)
- 深入浅出Liferay Portal (4)
- 深入浅出Liferay Portal (2)
- 深入浅出Liferay Portal (3)
- 深入浅出Liferay Portal (5)
- 深入浅出Liferay Portal (6)
- 深入浅出Liferay Portal (7)
- 深入浅出Liferay Portal (8)
- 深入浅出Liferay Portal (9)
- 深入浅出Liferay Portal (10)
- 深入浅出Liferay Portal (11)
- 深入浅出Liferay Portal (12)
- 深入浅出Liferay Portal (5)
- 深入浅出Liferay Portal (6)
- 深入浅出Liferay Portal (7)
- 深入浅出Liferay Portal (3)
- 深入浅出Liferay Portal (1)
- 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
- 深入浅出Liferay Portal (2)
- Cookies欺骗与session欺骗的实例入侵
- 深入浅出Liferay Portal (3)
- flex解析xml两方式
- 深入浅出Liferay Portal (4)
- 形参长度可变化的方法
- 面向对象思想
- 基于角色的权限管理(转)
- 学习
- 呃~~招免费测试的哈,嘿嘿
- JVM加载class文件的原理机制
- 深入浅出Liferay Portal (5)
- 常用CSS缩写语法总结