Portal 是一种非常棒的web2.0技术,它基于JSR168 Java Portlet规范, 用户可以根据自己的喜好加载所需的Portlet。它提供给用户非常个性化的用户体验。
GWT 是一种由Google公司所开发的基于Java的Web框架技术,用于开发全Ajax应用程序。
如何让这两种诱人的技术整合在一起就是我所感兴趣的,同样也是这篇文章所要探讨的主题。现有的主流Portal平台技术有Liferay, Jboss Portal, JetSpeed-2,exo等,而这篇文章将会着重介绍如何使用Liferay Portal来整合GWT。
首先你必须要安装好GWT,你可以在Google Code网站上下载到GWT的最新版本http://code.google.com/webtoolkit/
本例子里使用的是gwt-windows-1.4.60版本。直到我发布Blog文章时GWT的最新版本为gwt-windows-1.4.61。我看过了ChangeLog其实只是更改了一些在Mac下的bugs,并没有太大的改动。所以我暂时没有更新。
其次就是要安装Portal平台了,之前已经说过主流的Portal平台技术有Liferay, Jboss Portal, JetSpeed-2, exo。其实每一个Portal都可以和GWT整合,我最初做的整合的Example就是在JBoss Portal上(说实话在所有的Portal平台中我最喜欢的就是JBoss Portal),之后在每个平台上都成功的整合了。因为考虑到国内大多数Portal开发者都习惯使用Liferay,所以在这里就探讨一下 Liferay于GWT的整合方式。 Liferay Portal是一个基于MIT License的免费的开源Portal项目,你可以在Liferay的官方网站上下载到http://www.liferay.com/。最新的版本是在2008年2月份才发布的Liferay Portal 4.4.0。我也是更新到了这个最新版本的Tomcat5.5.20绑定版。
接下来我就根据我制作的一个简单的时钟的Example,来说明整合实现的方式(例子可以在文章底部下载到)
项目结构
GWT客户端的实现方式:
- package mqj.web.coral.client;
- import mqj.web.coral.client.rpc.CoralService;
- import com.google.gwt.core.client.EntryPoint;
- import com.google.gwt.user.client.Timer;
- import com.google.gwt.user.client.Window;
- import com.google.gwt.user.client.rpc.AsyncCallback;
- import com.google.gwt.user.client.ui.Label;
- import com.google.gwt.user.client.ui.RootPanel;
- /**
- * @author maqujun
- *
- */
- public class Coral implements EntryPoint {
- /** Timer is a serialized object of GWT */
- private Timer timer;
- private Label label = new Label("Wait...");
- /**
- * GWT Locat Time and set to the GWT label.
- * @author maqujun
- */
- private class CallBack implements AsyncCallback {
- public void onFailure(Throwable caught) {
- timer.cancel();
- Window.alert(caught.getMessage());
- }
- public void onSuccess(Object result) {
- label.setText(((Time) result).getTime());
- }
- }
- private CallBack callBack = new CallBack();
- /**
- * Running a Timer to collect time from GWT Server.
- */
- public void onModuleLoad() {
- <SPAN style="COLOR: #ff0000">RootPanel.get("uid").add(label);</SPAN>
- timer = new Timer() {
- public void run() {
- CoralService.App.getInstance().getTime(callBack);
- }
- };
- timer.scheduleRepeating(1000);
- }
- }
gwt.xml配置文件的代码
- <module>
- <inherits name='com.google.gwt.user.User' />
- <entry-point class='mqj.web.coral.client.Coral' />
- <servlet path="/CoralService" class="mqj.web.coral.server.CoralServiceImpl" />
- </module>
Coral.html文件代码
- <html>
- <head>
- <title>Coral</title>
- </head>
- <body>
- <script language='javascript' src='mqj.web.coral.Coral.nocache.js'></script>
- <SPAN style="COLOR: #ff0000"><div id='uid'></div></SPAN>
- </body>
- </html>
GWT服务器端的实现代码
- package mqj.web.coral.server;
- import com.google.gwt.user.server.rpc.RemoteServiceServlet;
- import java.text.DateFormat;
- import java.util.Date;
- import mqj.web.coral.client.Time;
- import mqj.web.coral.client.rpc.CoralService;
- /**
- * Server side of GWT time project.
- * @author maqujun
- *
- */
- public class CoralServiceImpl extends RemoteServiceServlet implements
- CoralService {
- /**
- * Collect time.
- */
- public Time getTime() {
- String out = DateFormat.getDateTimeInstance(DateFormat.SHORT,
- DateFormat.FULL).format(new Date());
- return new Time(out);
- }
- }
GWT Portlet的实现代码
- package mqj.web.coral;
- import javax.portlet.GenericPortlet;
- import javax.portlet.PortletException;
- import javax.portlet.PortletSecurityException;
- import javax.portlet.RenderRequest;
- import javax.portlet.RenderResponse;
- import java.io.IOException;
- import java.io.PrintWriter;
- /**
- * @author maqujun
- *
- */
- public class CoralPortlet extends GenericPortlet {
- protected void doView(RenderRequest renderRequest,
- RenderResponse renderResponse) throws PortletException,
- PortletSecurityException, IOException {
- renderResponse.setContentType("text/html");
- PrintWriter writer = renderResponse.getWriter();
- <SPAN style="COLOR: #ff0000">writer.println("<script language='javascript' src='"
- + renderRequest.getContextPath()
- + "/mqj.web.coral.Coral.nocache.js'></script>");
- writer.println("Coral's Time:");</SPAN>
- writer.println("<div id='uid'></div>");
- writer.close();
- }
在上面贴出的代码里我标注为红色的代码是要特别注意的地方,这是Portlet可以调用GWT内容的关键。所以GWT控件必须被定义在Div中。
Portlet的配置XML
liferay-display.xml
- <?xml version="1.0"?>
- <display>
- <category name="category.sample">
- <portlet id="gwt_portlet" />
- </category>
- </display>
liferay-portlet.xml
- <?xml version="1.0"?>
- <liferay-portlet-app>
- <portlet>
- <portlet-name>gwt_portlet</portlet-name>
- <instanceable>true</instanceable>
- </portlet>
- <role-mapper>
- <role-name>administrator</role-name>
- <role-link>Administrator</role-link>
- </role-mapper>
- </liferay-portlet-app>
portlet.xml
- <?xml version="1.0"?>
- <portlet-app
- xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
- version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">
- <portlet>
- <portlet-name>gwt_portlet</portlet-name>
- <display-name>Coral GWTPortlet</display-name>
- <portlet-class>mqj.web.coral.CoralPortlet</portlet-class>
- <supports>
- <mime-type>text/html</mime-type>
- </supports>
- <portlet-info>
- <title>Sample GWTPortlet</title>
- <short-title>Sample GWTPortlet</short-title>
- <keywords>Sample GWTPortlet</keywords>
- </portlet-info>
- <security-role-ref>
- <role-name>administrator</role-name>
- </security-role-ref>
- </portlet>
- </portlet-app>
以及web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app>
- <display-name>gwt_portlet</display-name>
- <servlet>
- <servlet-name>CoralService</servlet-name>
- <servlet-class>mqj.web.coral.server.CoralServiceImpl</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>CoralService</servlet-name>
- <url-pattern>/CoralService</url-pattern>
- </servlet-mapping>
- </web-app>
以上的Portal xml和web.xml的配置是这个Portlet能够正常运行在Liferay中最基本的信息。
更多的代码在这里就不贴出来了,有兴趣的朋友可以看一下附件中的源代码。
使用Ant打包GWT为war文件,以admin权限登陆Liferay, 使用Portlet plugin加载war文件。然后通过Add Application加载到你的页面中即可运行。
运行的图片是这样的
已知的问题:
这样的整合只能用于普通的GWT Portlet项目的开发,如果你想使用GWT-EXT这样的GWT与EXT整合的实现方式,即你想要实现Portal+GWT+EXT2.0的开发模式时会有问题。我尝试过。问题的关键点在于Div的使用上,等我解决了这个问题后我会把我的解决方案发布出来与大家共享。