Apache Pluto 和 Apache Geronimo:快速入门
Apache Pluto 是 Java™ Portlet Specification (JSR 168) 的参考实现。Pluto 将提供 JSR 168 中指定的基本轻量级容器接口实现和带有用于开发标准 portlet 的其他实用程序的门户驱动器。Pluto 用作一个基本的 portlet 容器,用于实现 portlet API 并为开发人员提供一个可以托管 portlet 的工作示例平台。Pluto 的简单门户组件只是基于 portlet 容器和 JSR 168 的要求构建的。
Apache Geronimo 1.1 是 Java 2 Platform, Enterprise Edition (J2EE) 1.4 认证的开源应用服务器。不同于其他 J2EE 应用服务器,Geronimo 预先集成了外部资源组件,例如数据库、消息传递服务器和目录服务器。Geronimo 是基于具有高定制性且模块化的架构构建的。它用作支持现有组件的框架来构成包含 30 多个最优供给(best-of-breed)开源项目的完整 J2EE 应用服务器包。Geronimo 预先集成了 Apache Tomcat 或 Apache Jetty;本文使用的是配有 Tomcat 的 Geronimo 版本。
虽然 Apache Pluto 本身就是基于 J2EE 标准构建的 Web 应用程序,但是它不能按原样直接部署。此外,开发的所有 Pluto 门户应用程序都不能被直接安装到 Pluto 门户容器中。开发人员通常把 Pluto 部署到 Apache Tomcat Web 容器中,但这并不是您的惟一选择。Geronimo 也可以托管 Pluto 应用程序。本文将展示如何把 Pluto 门户服务器与 Geronimo 结合使用,为后台配有功能丰富的常用应用服务器的门户应用程序提供一个完整的开源测试和部署环境。
在 Geronimo 中部署和执行 Web 应用程序的过程与在 Tomcat 中部署和执行 Web 应用程序的过程不同。本文的其余部分将向您展示如何通过以下步骤在托管在 Geronimo 上的 Pluto 中部署和执行样例门户应用程序:
-
通过
sharedlib模块共享 Pluto 库和属性。 - 为 Pluto 容器和驱动器创建 Geronimo 部署计划。
- 在 Geronimo 中部署 Pluto。
- 开发样例门户应用程序。
- 为样例门户应用程序创建必需的部署计划。
- 在 Geronimo 上部署和测试样例门户应用程序。
开始时,需要先下载、安装并解压缩 Geronimo 和 Pluto(有关下载链接,请参阅 参考资料)。我将把 Geronimo 的安装目录引用为 GERONIMO_HOME。您可以下载源代码版本或二进制版本的 Pluto。根据 Pluto 安装指南中的说明:“安装源代码版本要求完成更多工作,并且仅建议那些有兴趣修改容器的个人安装”,我将已下载的二进制版本的位置引用为 PLUTO_HOME 并将源代码版本位置引用为 PLUTO_SRC(当我向您指出 PLUTO_HOME 中的文件位置时,如果您已经构建了源文件版本,则可以定位相同的文件)。
在 Tomcat 中,Pluto 将把门户应用程序作为 Pluto 容器 Web 应用程序的子部分进行部署。这种方法在 Geronimo 并中不可行。部署新应用程序意味着创建新的 Web 应用程序和 Geronimo 部署计划。但是,没有一种方法能够在部署应用程序后动态更新计划和部署配置。解决这个问题的一种方法是把所有门户应用程序作为外部应用程序来部署,并且在 Pluto 中,配置新门户应用程序并被重定向到外部应用程序。配置包括在 Pluto 注册库中定义 portlet 和门户。这将解决最初遇到的问题,但是也会带来一大堆新问题。
一个主要的问题是,由于应用程序都是分别部署的,因此它们全都使用不同的类装入程序。这意味着 Portlet API 类、Pluto 容器类和常用服务类都是装入到 Pluto 应用程序和门户应用程序的独立类装入程序中,导致出现很多 ClassNotFoundException。
要解决此问题,需要把所有常用 Pluto 文件装入到同一个类装入程序中。Geronimo 1.1 中名为 sharedlib 的服务使您可以完成此操作。使用此项服务,您可以把常见类文件和库存储到共享文件夹中。需要使用共享文件的应用程序可以把自身配置为依赖于 sharedlib 服务来使用它。
要在 Pluto 中使用 sharedlib,请把以下文件从 PLUTO_HOME\webapps\pluto\WEB-INF\classes 复制到 GERONIMO_HOME\var\shared\classes 文件夹:
- pluto-admin.properties
- castor.properties
- logging.properties
并把以下文件从 PLUTO_HOME\shared\lib 和 PLUTO_HOME\webapps\pluto\WEB-INF\lib 文件夹复制到 GERONIMO_HOME\var\shared\lib 文件夹:
- pluto-1.0.1.jar
- pluto-deploy-1.0.1.jar
- pluto-descriptors-1.0.1.jar
- pluto-portal-1.0.1.jar
- portlet-api-1.0.jar
- castor-0.9.5.3.jar
- commons-fileupload-1.1.jar
- commons-io-1.1.jar
确保当 Geronimo 服务器启动时 geronimo/sharedlib/1.1.1/car 服务已处于启动状态。
如果 Geronimo 中的所有应用程序使用外部资源引用(例如安全配置或依赖性因素),则需要使用 Geronimo 部署计划。正如您刚刚学到的那样,Pluto 容器和驱动器应用程序必须部署到 Geronimo 中,并且必须与 sharedlib 服务具有依赖性。Pluto 容器和驱动器应用程序以及 Pluto 服务器应用程序都是 Web 应用程序,因此它们的部署计划都将是 Geronimo Web 部署计划。
Geronimo 部署计划与 J2EE 部署描述符有些相像;不同之处在于 Geronimo 部署计划包含特定于服务器的信息。清单 1 显示的是常用 Pluto 服务器应用程序的 Geronimo Web 部署计划。
清单 1. Geronimo 的 Pluto 部署计划 —— geronimo-web-pluto.xml
<?xml version="1.0"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/web"
xmlns:naming="http://geronimo.apache.org/xml/ns/naming"
xmlns:tomcat="http://geronimo.apache.org/xml/ns/web/tomcat/config-1.0"
xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1">
<sys:environment>
<dependencies>
<dependency>
<artifactId>sharedlib</artifactId>
</dependency>
</dependencies>
</sys:environment>
<security-realm-name>pluto-properties-file-realm</security-realm-name>
<security>
<default-principal realm-name="pluto-properties-file-realm">
<principal class=
"org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" name="user"/>
</default-principal>
<role-mappings>
<role role-name="tomcat">
<realm realm-name="pluto-properties-file-realm">
<principal class=
"org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"
name="admin"/>
<principal class=
"org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal"
name="system"/>
</realm>
</role>
</role-mappings>
</security>
<tomcat:cross-context/>
</web-app>
|
您可以看到清单 1 中的部署计划有三个主要配置:
-
依赖性:
<dependency>元素用于指定 Web 应用程序与任何其他模块、应用程序或服务之间的依赖性。它还可以指明与位于 Geronimo 存储库中的第三方库之间的依赖性。在 清单 1 中,<dependency>元素内的<artifactId>sharedlib</artifactId>将指定与sharedlib服务之间的依赖性。 -
安全性:Pluto Web 应用程序要求配置安全性。特别是,它要求使用
<security-realm>验证用户登录和主要角色,并使用<role-mappings>映射在 Pluto 的 Web 部署描述符 (web.xml) 中定义的角色。在 清单 1 中,pluto-properties-file-realm安全领域被配置为强制执行特定于应用程序的验证策略,它用作登录域的入口点。它被配置为使用org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal。在 Pluto 应用程序的 web.xml 文件中定义的tomcat角色被映射为使用这个security-realm。 -
跨上下文:Pluto 服务器要求使用的一项重要功能是能够调用部署在 Geronimo 服务器上的其他 Web 应用程序中提供的 portlet。默认情况下,这个功能 —— 允许一个 Web 应用程序把请求分派给其他 Web 应用程序 —— 是禁用状态。您可以通过在部署计划中指定
<cross-context>元素来启用它。清单 1 中出现的<tomcat:cross-context/>将为 Pluto 服务器应用程序激活此功能。
如果已下载的是 Pluto 源代码版本,您可以将其构建为获得 Pluto Web 应用程序 PLUTO_SRC\pluto\portal\target\pluto.war。如果使用的是 Pluto 的二进制版本,请使用以下命令从 PLUTO_HOME\webapps\pluto 文件夹创建 pluto.war(其中 pluto_xxx 是您选定的文件夹):
c:\pluto_xxx>jar cvf pluto.war
拥有 pluto.war 之后,请使用 Web 控制台或命令行部署工具(在一行中键入以下命令)部署它:
c:\geronimo-1.1.1\bin>deploy --user system deploy
c:\pluto_xxx\pluto.war c:\pluto_xxx\geronimo-web-pluto.xml
作为一个演示门户应用程序的开发及部署的样例场景,您将开发一个购票应用程序。由于要开发的是基于 JSR 168 和 J2EE 的应用程序,因此对于部署在 Geronimo 中的 Pluto 的门户应用程序的服务器设置与所有其他服务器设置完全相同。要创建样例门户应用程序,需要创建四个文件:
- Portlet 类
- Portlet 描述符
- Web 应用程序描述符
- Portlet 视图页面
这些文件内容的详细说明不在本文讨论范围内。有关更多信息,请阅读 “使用 Apache Pluto 构建和测试 JSR 168 兼容的 portlets”(developerWorks,2006 年 4 月)。
门户类中的每个 portlet 都有一个用于实现 javax.portlet.Portlet 接口的 portlet 类。为了便于操作,JSR 168 为 javax.portlet.Portlet 定义了一个默认实现 —— javax.portlet.GenericPortlet 类。清单 2 显示了 org.sample.geronimo.buyticket.BuyTicketPortlet 类,它将扩展 BuyTicket portlet 的 GenericPortlet。同样地,本文的样例代码将包括 org.sample.geronimo.buyticket.TShirtPortlet(请参阅 下载 部分)。
清单 2. portlet 类 —— org.sample.geronimo.buyticket.BuyTicketPortlet
package org.sample.geronimo.buyticket;
import java.io.*;
import javax.portlet.*;
import org.apache.pluto.portlet.admin.util.PlutoAdminContext;
public class BuyTicketPortlet extends GenericPortlet {
private static String VIEW_JSP = "/view.jsp";
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletContext context = getPortletConfig().getPortletContext();
context.getRequestDispatcher(VIEW_JSP).include(request, response);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
String game = request.getParameter("game");
String name = request.getParameter("name");
String card = request.getParameter("card");
String errorMessage = null;
if(game != null && name != null && card != null
&& !game.equals("") && !name.equals("")
&& !card.equals("")){
response.setRenderParameter("game", game);
response.setRenderParameter("name", name);
}else
response.setRenderParameter("errorMessage",
"You entered invalid data, please check the name and credit information");
}
}
|
清单 3 中显示的 portlet.xml 文件将为门户应用程序中包含的所有 portlet 提供诸如 portlet 名称、标题、类和模式之类的信息。
清单 3. portlet 描述符 —— portlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
id="org.sample.geronimo.buyticket.BuyTicketPortlet">
<portlet>
<portlet-name>BuyTicket</portlet-name>
<display-name>Buy Ticket Portlet</display-name>
<portlet-class>
org.sample.geronimo.buyticket.BuyTicketPortlet
</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>Buy Ticket Title</title>
</portlet-info>
</portlet>
<portlet>
<portlet-name>TShirt</portlet-name>
<display-name>TShirt Portlet</display-name>
<portlet-class>
org.sample.geronimo.buyticket.TShirtPortlet
</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>TShirt</title>
</portlet-info>
</portlet>
</portlet-app>
|
web.xml 是用于提供关于 Web 应用程序组件信息(例如 servlet 和 taglib)的 J2EE 指定的标准 XML 描述符。清单 4 将显示 buyticket 门户应用程序的 WEB-INF\web.xml 文件。
清单 4. Web 应用程序描述符 —— WEB-INF\web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>GetPortlet</display-name>
<servlet>
<servlet-name>BuyTicket</servlet-name>
<display-name>BuyTicket Wrapper</display-name>
<description>Automated generated Portlet Wrapper</description>
<servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-guid</param-name>
<param-value>buyticket.BuyTicket</param-value>
</init-param>
<init-param>
<param-name>portlet-class</param-name>
<param-value>org.sample.geronimo.buyticket.BuyTicketPortlet</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>BuyTicket</servlet-name>
<url-pattern>/BuyTicket/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TShirt</servlet-name>
<display-name>TShirt Wrapper</display-name>
<description>Automated generated Portlet Wrapper</description>
<servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-guid</param-name>
<param-value>buyticket.TShirt</param-value>
</init-param>
<init-param>
<param-name>portlet-class</param-name>
<param-value>org.sample.geronimo.buyticket.TShirtPortlet</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>TShirt</servlet-name>
<url-pattern>/TShirt/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>tld/portlet.tld</taglib-location>
</taglib>
<taglib id="PortletTLD">
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>/WEB-INF/tld/portlet.tld</taglib-location>
</taglib>
</web-app>
|
BuyTicketPortlet 包括一个视图页面以通过调用 context.getRequestDispatcher("view.jsp").include(request, response); 来解除视图部分与 portlet 代码之间的耦合。清单 5 中显示的 view.jsp 文件用于呈现实际的 HTML 页面。
清单 5. 初始页面 —— view.jsp
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<portlet:defineObjects />
<%
String game = renderRequest.getParameter("game");
if (game == null) game = "";
String name = renderRequest.getParameter("name");
if (name == null) name = "";
String errorMessage = renderRequest.getParameter("errorMessage");
if (errorMessage != null) {
%>
<p><%=errorMessage%></p>
<%
}else if (name != null && !name.equals("") && game != null && !game.equals("") ) {
%>
<p><b> Thank you <%= name %>, you purchased ticket for <%= game %></b></p>
<%
}
%>
<FORM name="<portlet:namespace/>ticketform" action="<portlet:actionURL/>">
<table border="0">
<tr><td>
Select Game/Event
</td><td>
<select name="game">
<%
java.util.ArrayList map=org.sample.geronimo.buyticket.GameConstants.map;
java.util.Iterator itr=map.listIterator();
while(itr.hasNext())
{
String gamelist=(String)itr.next();
%>
<option value=<%= gamelist %> selected="true"><%= gamelist %></option>
<%
}
%>
</select>
</td></tr>
<tr><td>Name</td><td><INPUT type="text" name="name" size="20"/></td></tr>
<tr><td>Card</td><td><INPUT type="text" name="card" size="20"/></td></tr>
<tr><td>Exp. Date</td><td><INPUT type="text" name="date" size="20"></td></tr>
</table>
<INPUT type="submit" name="<portlet:namespace/>submitTicket" value="Buy"/>
<INPUT type="submit" value="Cancel"/>
</FORM>
|
现在把 portlet.tld 文件从 PLUTO_HOME\webapps\pluto\WEB-INF\tld 复制到 WEB-INF\tld 文件夹中。编译 WEB-INF\classes 文件夹中的 Java 文件,并把以上所有文件打包成一个 WAR 文件,如下所示:
c:\buyticket>jar -cvf buyticket.war
当 Pluto 被部署到 Geronimo 上时,必须把将要部署到 Pluto 中的所有门户应用程序作为 Web 应用程序部署到 Geronimo 服务器中。您需要有一个 buyticket 门户应用程序的 Geronimo Web 部署计划,就像需要有一个 Pluto 服务器应用程序 Web 部署计划一样。清单 6 中显示了 buyticket 的部署计划 —— geronimo-web-app.xml。
清单 6. 门户应用程序部署计划 —— geronimo-web-app.xml
<?xml version="1.0"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/web"
xmlns:naming="http://geronimo.apache.org/xml/ns/naming"
xmlns:tomcat="http://geronimo.apache.org/xml/ns/web/tomcat/config-1.0"
xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1">
<sys:environment>
<dependencies>
<dependency>
<artifactId>sharedlib</artifactId>
</dependency>
</dependencies>
</sys:environment>
</web-app>
|
Pluto 门户应用程序要求使用常见的 Pluto 文件,这些文件都是通过 Geronimo 中的 sharedlib 服务共享的。门户应用程序与 sharedlib 服务的依赖性是在清单 6 所示的门户应用程序部署计划中指定的。
门户应用程序必须执行两级部署。在第一级中,它将作为 Web 应用程序被部署到 Geronimo 中。您可以通过使用 Geronimo 部署工具轻松完成此操作:
- 在一行中键入以下命令:
c:\geronimo-1.1.1\bin>deploy --user system deploy c:\buyticket\buyticket.war
c:\buyticket\geronimo-web-app.xml
此命令将把应用程序作为 Web 应用程序部署到 Geronimo 中,但是要使用 Pluto 服务和功能,必须通过 Pluto 把应用程序重定向(第二个部署级别)。 - 为此,您必须把应用程序配置信息存储到 Pluto 注册库中。您可以手动更新 pluto\WEB-INF\data\pageregistry.xml、pluto\WEB-INF\data\portletcontexts.txt 和 pluto\WEB-INF\data\portletentityregistry.xml 文件,也可以执行下一步使用 Pluto admin 应用程序。
- 打开 http://localhost:8080/pluto/portal,并单击 Admin 链接。此操作将打开图 1 中所示的 http://localhost:8080/pluto/portal/adminportletapp 应用程序。
图 1. Pluto admin 应用程序 —— 部署 buyticket portlet 应用程序
- 在此应用程序中,浏览到 buyticket.war 文件,并单击 Submit。此操作将部署
buyticket应用程序并把门户应用程序的具体配置添加到 portletentityregistry.xml 中。 - 在 admin 部署的下一个页面中,请为新部署的门户应用程序提供标题、描述和布局信息(参见图 2)。
图 2. Pluto admin 应用程序 —— 门户布局
- 把行数和列数更改为显示门户应用程序所包含的行数和列数。
- 接下来,选择显示在每行每列的 portlet,如图 3 所示。
图 3. Pluto admin 应用程序 —— 定义页面布局
在此步骤中,使用门户应用程序中进行 portlet 定位的布局设置更新 pageregistry.xml。执行完此步骤就完成了把门户应用程序部署到在 Geronimo 上运行的 Pluto 服务器中的任务。 - 现在可以开始通过单击 buyticket 链接来测试和使用应用程序。图 4 将显示已部署到 Pluto 服务器中并且正在运行的
buyticket应用程序。
图 4. buyticket 门户应用程序
Apache Pluto 是用于 JSR 168 兼容性测试的易于使用的小型门户测试环境。门户服务器支持并未预先集成到 Geronimo 中,但是任何门户服务器 —— 例如 Pluto、Jetspeed 或 Liferay —— 都可以作为插件或外部应用程序安装到 Geronimo 中。即使您引入了重量级的 Jetspeed 或 Liferay 作为部署环境,也只有 Pluto 能够满足轻量级开发和测试的要求。通过把 Pluto 和 Geronimo 集成到一起,您将获得结合了开源门户服务器与应用服务器的精华的门户环境。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 样例应用程序 | buyticket.zip | 14KB | HTTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 访问 Apache Pluto 和 Apache Geronimo 项目的主页。
- 查阅 developerWorks Apache Geronimo 项目区,以获取文档、教程和其他参考资料以帮助您开始使用 Geronimo 进行开发。
- 阅读 “使用 Apache Pluto 构建和测试 JSR 168 兼容的 portlets”(developerWorks,2006 年 4 月),这是一篇进行 portlet 测试的指导文章。
- 了解关于 JSR 168 和 JSR 286(Java Portlet Specification 的下一个版本)的信息。
- 访问 developerWorks 开源软件技术专区,获得丰富的 how-to 信息、工具和项目更新,帮助您使用开放源码技术进行开发,并与 IBM® 产品结合使用。
- 访问 developerWorks 的 Apache Geronimo 入门 部分,查找对于初学者和有经验的用户都有帮助的参考资料。
- 查阅 IBM Support for Apache Geronimo 提供的参考资料,让您能够在世界级的 IBM 支持下开发 Geronimo 应用程序。
- 随时关注 developerWorks 的 技术事件和网络广播。
- 浏览 developerWorks 开放源码专区提供的所有 Apache 文章 和 免费 Apache 教程。
获得产品和技术
- 下载 Apache Geronimo 1.1 和 Apache Pluto 的免费的二进制版本和源代码版本。
- 用 Geronimo 1.1 的 Eclipse 插件 立即开始进行 Java 应用程序开发。
- 下载 IBM WebSphere® Application Server Community Edition 的免费副本 —— 基于 Apache Geronimo 开源技术构建的轻量级 J2EE 应用服务器,可用于帮助加速开发及部署工作。
- 使用 IBM 试用软件 改进您的下一个开发项目,这些软件可以通过下载或从 DVD 中获得。
讨论
- 参与论坛讨论。
- 加入 Pluto 讨论区 的用户邮件列表、开发人员邮件列表和源代码控制邮件列表。
- 参与 Pluto JIRA 主题讨论。
- 加入 Geronimo 1.1 讨论区 的用户邮件列表、开发人员邮件列表和源代码控制邮件列表。
- 参与 Geronimo 1.1 JIRA 主题讨论。
- 参与 developerWorks blog,加入 developerWorks 社区。
- 随时关注 Apache Geronimo blog,以了解 Geronimo 开发的最新进展。

Rakesh Midha 是 Bangalore 的 IBM 软件实验室的一名软件架构师,目前效力于 IBM 内部的开放源码技术。他有 8 年在多平台和各种关系数据库系统(像 IBM DB2® Universal Database™、Oracle、MySQL 和 Microsoft SQLServer)上进行 Java 和 C++ 服务器端编程的技术经验。他参与了多个 J2EE 应用程序、产品架构开发及实现。他是两本与 DB2 相关的 IBM 红皮书的作者,并经常在 IBM developerWorks 发表文章。2006 年 8 月,他参加了在斯里兰卡 Colombo 召开的 ApacheCon Asia 会议,并发表了主题为 “深入 Apache Geronimo 1.1 —— 是什么让它与众不同?” 的演讲。他从印度 Chandigarh 的 Punjab 大学获得了电子工程学学士学位。