MDA模式开发Struts Portlet
前言:
上一篇笔记中,我们在Ext环境中开发了一个简单的Struts Portlet,在这一篇中我们将使用MDA模式开发一个较为复杂的Struts Portlet,这也是我们在实际项目中常用的开发方式。
一. 开发流程
1. 编写并执行service.users.xml,生成实体类和持久化类。(这一步和Hibernate Engine的功能很类似,感觉Hibernate Enine的更简易一些)
2. 增加业务代码接口和实现
3. 增加Portlet
4. 配置Portlet
基本流程就这四步,看似很简单,但中间的详细步骤是比较多而且复杂的。
二. 详细开发步骤(实例)
a) 在数据库新建一个表my_users,并插入模拟数据。
create table my_users(userid varchar(30),username varchar(30),password varchar(30))
insert into my_users values('001','zhangsan','1234');
insert into my_users values('002','lisi','1234');
insert into my_users values('003','ww','1234');
b) 在ext-impl文件夹下新建一个services文件夹,我们将所有的service.xml都放入此文件夹。
c) 在新建的services.xml中创建service.users.xml,文件内容为:(可以拷贝一个例子中的service.xml进行修改)
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 4.3.3//EN" "http://www.liferay.com/dtd/liferay-service-builder_4_3_3.dtd">
<service-builder package-path="com.ext.portlet.users">
<namespace>Users</namespace>
<entity name="UsersEntry" local-service="false"
remote-service="true" persistence-class="com.ext.portlet.users.service.persistence.UsersEntryPersistenceImpl">
<!-- PK fields -->
<column name="userid" type="String" primary="true" />
<!-- Audit fields -->
<column name="username" type="String" />
<column name="password" type="String" />
<!-- Order -->
<order by="asc">
<order-column name="username" case-sensitive="false" />
</order>
</entity>
<exceptions>
<exception>UsersEntry</exception>
</exceptions>
</service-builder>
d) 修改ant文件,增加ant任务:
打开ext-impl目录下的build.xml,增加一个任务
<target name="build-service-portlet-users">
<antcall target="build-service">
<param name="service.file" value="services/service.users.xml" />
</antcall>
</target>
e) 执行build.xml的build-service-portlet-users任务
f) 打开ext-impl/src/META-INF/ext-hbm.xml,修改表名。(这是什么?噢,hibernate映射文件。^_^是不是很熟悉)
把<class name="com.ext.portlet.users.model.impl.UsersEntryImpl" table="UsersEntry">改为
<class name="com.ext.portlet.users.model.impl.UsersEntryImpl" table="my_users">
g) 增加业务方法并实现
打开/ext-service/src/com/ext/portlet/users/service下UsersEntryService文件,建立一个名为getAllUsers的抽象方法;
public List getAllUsers() throws SystemException;
在/ext-impl/src/com/ext/portlet/users/service/impl/UsersEntryServiceImpl.java中实现它.
public List getAllUsers() throws SystemException {
return UsersEntryUtil.findAll();
}
h) 重新执行build.xml的build-service-portlet-users任务
工具会帮你在UsersEntryServiceUtil也将业务方法实现,我们的Action层将会直接使用UsersEntryServiceUtil来访问持久层。
i) 在/src/com/ext/portlet/users/action目录下建立一个ViewUserAction.java文件,完成响应客户端请求。
package com.ext.portlet.users.action;
import java.util.ArrayList;
import java.util.List;
import javax.portlet.PortletConfig;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.WindowState;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.ext.portlet.users.model.UsersEntryModel;
import com.ext.portlet.users.service.UsersEntryServiceUtil;
import com.liferay.portal.struts.PortletAction;
public class ViewUsersAction extends PortletAction {
public ActionForward render(ActionMapping mapping, ActionForm form,
PortletConfig config, RenderRequest req, RenderResponse res)
throws Exception {
if (req.getWindowState().equals(WindowState.NORMAL)) {
return mapping.findForward("portlet.ext.users.view");
} else {
List users = UsersEntryServiceUtil.getAllUsers();
List usernames = new ArrayList();
for (int i = 0; i < users.size(); i++) {
usernames.add(((UsersEntryModel) users.get(i)).getUsername());
}
req.setAttribute("users", usernames);
req.setAttribute("count", ""+users.size());
return mapping.findForward("portlet.ext.users.view_users");
}
}
}
j) 建立Portlet
建立自己的portlet。在/ext-impl/src/com/ext/portlet/users下面新建文件UsersPortlet.java
package com.ext.portlet.users;
import java.io.IOException;
import javax.portlet.PortletException;
import javax.portlet.PortletPreferences;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.liferay.portlet.StrutsPortlet;
public class UsersPortlet extends StrutsPortlet {
public void doView(RenderRequest req, RenderResponse res)
throws IOException, PortletException {
PortletPreferences prefs = req.getPreferences();
System.out.println(prefs.getValue("test", ""));
super.doView(req, res);
}
}
该portlet重载了doview方法。如果自己portlet对view和edit有自己特性,那么只要重载doview和doedit就可以了。
现在portlet的内容是空的,我们用jsp页面来展示用户名称方式来填充portlet里面内容。
k) 编写View层JSP文件:init.jsp,view.jsp,view_users.jsp
(你知道在哪建的^_^)
init.jsp
<%@ include file="/html/common/init.jsp" %>
<portlet:defineObjects />
<%
PortletPreferences prefs = renderRequest.getPreferences();
%>
view.jsp
<%@ include file="/html/portlet/ext/users/init.jsp" %>
<a href="<portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>" />">My User<%= prefs.getValue("user", "") %></a>
view_users.jsp
<%@ include file="/html/portlet/ext/users/init.jsp" %>
<%List users = (List)request.getAttribute("users");%>
<%
for (int i = 0; i < users.size(); i++) {
String userName = (String)users.get(i);
%>
<%= userName %><br>
<%
}
%>
l) 配置porlet(胜利在望…加油)
m) 打开portlet-ext.xml,增加一个portlet配置(这个配置文件你已经很熟悉了)
<portlet>
<portlet-name>EXT_Users</portlet-name>
<display-name>Users</display-name>
<portlet-class>
com.ext.portlet.users.UsersPortlet
</portlet-class>
<init-param>
<name>view-action</name>
<value>/ext/users/view_users</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
</supports>
<resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>
<portlet-preferences>
<preference>
<name>test</name>
<value>user portlet</value>
</preference>
</portlet-preferences>
<security-role-ref>
<role-name>power-user</role-name>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
</security-role-ref>
</portlet>
n) 配置liferay-portlet-ext.xml
<portlet>
<portlet-name>EXT_Users</portlet-name>
<struts-path>ext/users</struts-path>
<use-default-template>false</use-default-template>
</portlet>
o) 配置liferay-display.xml
在example分类中加入:
<portlet id="EXT_Users" />
p) 在struts-config.xml中加入
<!-- Users -->
<action path="/ext/users/view_users" type="com.ext.portlet.users.action.ViewUsersAction">
<forward name="portlet.ext.users.view" path="portlet.ext.users.view" />
<forward name="portlet.ext.users.view_users" path="portlet.ext.users.view_users" />
</action>
q) 在tiles-defs.xml中加入:
<!-- Users -->
<definition name="portlet.ext.users.view" extends="portlet">
<put name="portlet_content" value="/portlet/ext/users/view.jsp" />
</definition>
<definition name="portlet.ext.users.view_users" extends="portlet">
<put name="portlet_content"
value="/portlet/ext/users/view_users.jsp" />
</definition>
r) 修改国际化属性文件Language-ext.properties,加入
javax.portlet.title.EXT_Users=Users
view-users=View Users
s) 完工! 重新部署后就可以测试了。目前功能只有查找所有用户姓名的业务方法和View。剩下的增删改自己完善吧。
开发过程中遇到的错误及解决方法:
一.portlet not found 警告
“以前开发了一个portlet,并在一个页面中使用了它。后来由于重新生成了ext,该portlet丢失,服务器启动没问题,访问页面也没有问题。但是在访问某个页面的时候,总在tomcat的控制台上打印警告:Portlet Not Found for ....,虽然说没什么影响,但是看上去非常不爽。为了解决这个问题,查阅了一些资料。发现之所以出现这个警告,是因为以前不同页面的布局都是保存在数据库当中,每次访问页面时,都会调用数据库中记录的页面对应的portlet,添加了我开发的portlet的那个页面也一样,但是重新生成ext后,那个portlet不存在了,而数据库页面的布局中还保存有该portlet的记录,所以报出警告。解决方法也很简单,只要找到数据库中的layout表,在typeSetting字段中删除那个portlet即可。”
二.JSP空指针异常
18:15:03,578 ERROR [[jsp]:723] Servlet.service() for servlet jsp threw exceptio
java.lang.NullPointerException
at com.liferay.portlet.PortletConfigImpl.getResourceBundle(Unknown Sour
e)
at com.liferay.portal.util.PortalUtil.getPortletTitle(Unknown Source)
at com.liferay.portal.util.comparator.PortletTitleComparator.compare(Un
nown Source)
at java.util.Arrays.mergeSort(Arrays.java:1284)
at java.util.Arrays.sort(Arrays.java:1223)
at java.util.Collections.sort(Collections.java:159)
at org.apache.jsp.html.portlet.layout_005fconfiguration.view_005fcatego
y_jsp._jspService(view_005fcategory_jsp.java:781)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrappe
.java:328)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:
15)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(App
icationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(Application
ilterChain.java:188)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDis
atcher.java:691)
at org.apache.catalina.core.ApplicationDispatcher.doInclude(Application
ispatcher.java:594)
at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDi
patcher.java:505)
at com.liferay.taglib.util.IncludeTag.doEndTag(Unknown Source)
at org.apache.jsp.html.portlet.layout_005fconfiguration.view_jsp._jspx_
eth_liferay_002dutil_005finclude_005f0(view_jsp.java:875)
at org.apache.jsp.html.portlet.layout_005fconfiguration.view_jsp._jspSe
vice(view_jsp.java:791)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrappe
.java:328)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:
15)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(App
icationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(Application
ilterChain.java:188)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDis
atcher.java:691)
at org.apache.catalina.core.ApplicationDispatcher.doInclude(Application
ispatcher.java:594)
at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDi
patcher.java:505)
at com.liferay.taglib.util.IncludeTag.doEndTag(Unknown Source)
at org.apache.jsp.html.common.themes.portlet_jsp._jspService(portlet_js
.java:925)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrappe
.java:328)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:
在官方网站上搜索后,发现有人遇到过类似问题,并且有人给出了解决办法:
https://www.liferay.com/zh/community/forums/-/message_boards/message/155212
在此引用下回答,我觉得很有趣:
I had the same problem as I was also following the tutorials on wiki.liferay.com.
The issue comes up if you define the resource-bundle tag of your portlet in portlet-ext.xml like this:
<resource-bundle>
com.liferay.portlet.StrutsResourceBundle
</resource-bundle>
If you want to avoid this problem define it in one line like this:
<resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>
I know this sounds stupid but this work. I resolved this problem by hit and trial. I hope liferay guys resolve this issue in the next release.