OSGI(Open Service Gateway Initiative)在java世界中,提供了一种从机制上进行模块划分的理念和实践,对于java的模块化生产将会产生不可估量的推动作用。
由于OSGI是从嵌入式开发中发展起来的,在Eclipse3.0以后的Equinox实现了OSGI的标准后,OSGI开发模式在java世界中开始流行起来。但是对于企业级的应用,特别是多层的企业级应用,现在流行的OSGi实现Equinox、Felix都提供了实现的方式,在Application Server中嵌入OSGI的运行环境,从而实现Server端的OSGI。在OSGI框架中,推荐的方式是利用应用服务器的OSGI实现,在统一的OSGI模式下,进行WEB应用的部署,但是,任何的技术应用都不是孤立存在和从零开始的,大量的传统应用的融合和继承,注定了第一种模式的生命力。
文章《使用 Felix 和 Struts2 开发 Web 应用》中详细的介绍了利用Felix框架,开发Struts2应用的过程,美中不足的是,原文没有提供真正的实现工程,在依照这一过程实现中,出现了很多意想不到的情况。
下面把我实现一个完整的应用的过程进行梳理:
1、建立接口插件工程:利用Eclipse的标准过程进行,只是在生成工程的过程中,选用OSGI的Standard模式,填入工程名为:com.example.time,在其中生成一个将来用于交互的接口。
发布出来的接口的代码如下,只是一个示例性的例子,获得一个时间字符串。
package com.example.time.service; public interface TimeService{ public String getTime(); }
在MANIFEST.MF文件中,把包com.example.time.service发布出去,供其他插件应用。
2、生成提供local和utc时间服务的插件。
时间服务提供插件生成插件的过程和生成接口插件过程相同,插件分别命名为com.example.time.local和com.example.time.utc,在其中实现的过程如下:
2.1 插件向外提供服务的方式
OSGI的插件有两种向外部提供服务的方式,一是通过导出包,如上面的接口插件工程,就是导出了一个包。二是通过提供服务的方式,在插件中发布出提供的服务,供其它插件消费。在OSGI中,第二种方式是被鼓励应用的,因为这样对于实现系统热插拔具有很大的帮助。在本实验中,时间服务就是利用第二种方式提供的。
2.2 com.example.time.local
在插件中导入com.example.time.service包,然后TimeService的代码如下:
package com.example.time.local.service; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import com.example.time.service.TimeService; public class LocalTimeService implements TimeService { @Override public String getTime() { Calendar calendar = Calendar.getInstance(); Date date = calendar.getTime(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return "The local time:" + formatter.format(date); } }
获得了本地的时间,并转换为String的格式,向外提供。
在插件中,发布服务的方式有多种,可以再代码中发布,也可以通过声明发布,还可以通过springdm管理发布,由于本例中非常简单,就选有了,利用代码发布的方式,在activator的Start方法中,发布服务:
public void start(BundleContext context) throws Exception { context.registerService(TimeService.class.getName(), new LocalTimeService(), null); }
2.3 com.example.time.utc
utc服务发布插件的实现过程和local服务发布的插件实现过程相同,utc时间的服务类为:
package com.example.time.utc.service; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import com.example.time.service.TimeService; public class UTCTimeService implements TimeService { @Override public String getTime() { Calendar calendar = Calendar.getInstance(); int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); int dstOffset = calendar.get(Calendar.DST_OFFSET); calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset)); Date date = calendar.getTime(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm s"); return "The UTC time:" + formatter.format(date); } }
3、web bundle
通过上面的三个bundle,实现了对于local和utc时间服务的发布,下面就要在web bundle中,对上面服务的服务进行应用了,为了在web bundle中支持struts2,需要在web bundle的MANIFEST.MF文件中,进行以下的设置:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Web Bundle-SymbolicName: com.example.time.web Bundle-Version: 1.0.0.qualifier Bundle-Vendor: EXAMPLE Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Private-Package: time,. Struts2-Enabled: true Import-Package: com.example.time.service, com.opensymphony.xwork2, org.apache.struts2.osgi.interceptor, org.osgi.framework;version="1.3.0" Export-Package: com.example.time.web.action
其中Struts2-Enable设置为true,Private-Package设置web中页面的放置位置。
在bundle中,实现一个Action,从ActionSupport继承,实现BundleContextAware接口。BunndleContextAware中定义了一个函数:setBundleContext,在其中Action可以获得bundle的Context,从而访问其他bundle服务出来的服务:
package com.example.time.web.action; import org.apache.struts2.osgi.interceptor.BundleContextAware; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import com.example.time.service.TimeService; import com.opensymphony.xwork2.ActionSupport; public class TimeAction extends ActionSupport implements BundleContextAware{ private String jsonText = "json text"; private BundleContext bundleContext; private String timeMessage; public String execute(){ ServiceReference ref = bundleContext.getServiceReference( TimeService.class.getName()); TimeService timeService = (TimeService) bundleContext.getService(ref); timeMessage = timeService.getTime(); return SUCCESS; } public String getJsonText() { return jsonText; } public void setJsonText(String jsonText) { this.jsonText = jsonText; } public String getTimeMessage() { return timeMessage; } public void setTimeMessage(String timeMessage) { this.timeMessage = timeMessage; } public void setBundleContext(BundleContext arg0) { // TODO Auto-generated method stub this.bundleContext = arg0; } }
为了应用ActionSupport和BundleContextAware,需要导入相应的包,在Eclipse默认的OSGi环境中,是没有plugin提供这两个包的,需要在OSGi的Target Platform中,设置这些bundle。
在Target Platform中加入OSGI需要的包,在windows->Preferrences出现对话框,选择Plugin development中的Target Platform,默认情况下只有${eclipse-home}目录,为了应用struts2,在其中加入Struts2的lib目录,则Struts2自带的bundle就可以在工程中应用了。
在time目录下定义freemarker文件time.ftl:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Untitled Document</title> </head> <body> <h1> Hello world,Bundle test.${timeMessage}</h1> </body> </html>
最后进行struts文件的定义:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "file:D:/software/struts-2.2.1/src/core/src/main/resources/struts-2.0.dtd"> <struts> <package name="time-example" namespace="/time" extends="osgi-default"> <action name="time" class="com.example.time.web.action.TimeAction"> <result type = "freemarker">/time/time.ftl</result> </action> </package> </struts>
就完成web bundle了。