一、契子
很早以前就开始构思可动态部署的Web应用,模块化应用无疑是一种趋势,Portal应用可谓是一个小革新,它的功能引起了很多人的注意,OSGi 无疑会为这带来本质上的升级。
二、目标
这篇blog中的例子从JPetStoreOsgi衍生,通过扩展(修改)Spring mvc中的某些对象,实现模块的动态部署,当然,这只是很简单的案例,不过足以达到我的预期目标:有2个非常简单的模块module1和module2,它们都有自己的Spring mvc配置文件,可以在运行时简单的通过OSGi控制台,安装它们,并完成它们各自的功能。
三、准备工作
[点击这里下载 DynamicModule 工程包]
由于整个Workspace太大,所以仅仅只是把更新的5个Bundle的Project上传了,先 下载JPetStoreOsgi ,然后将所有关于JPetStore的Project删除,导入这5个Project
四、Spring MVC
目前还没有用于OSGi环境的MVC框架,所以选用Spring MVC做为演示框架
org.phrancol.osgi.demo.mvc.springmvc 是整个应用的MVC Bundle,以下简称 MVCBundle
org.phrancol.osgi.demo.mvc.springmvc.core.HandlerRegister
publicinterfaceHandlerRegister
{
/** *//**
* 当bundle的ApplicationContext生成后,获取HandlerMapping,并注册
*@paramcontext Spring为Bundle生成的ApplicationContext
*@parambundle
*/
publicvoidregisterHandler(ApplicationContext context, Bundle bundle);
/** *//**
* 当Bundle被停止或是卸载的时候,注销这个bundle的HandlerMapping
* 当然这个功能没有实现(它可以实现),因为他不属于演示范围
*@parambundle
*/
publicvoidunRegisterHandler(Bundle bundle);
}
扩展DispatcherServlet - org.phrancol.osgi.demo.mvc.springmvc.core.OsgiDispatcherServlet
同时,它还充当一个HandlerMapping注册管理器的角色,通过一个BundleHandlerMappingManager来管理bundle的HandlerMapping,包括动态添加/删除等,它会重写DispatcherServlet 的getHandler方法,从BundleHandlerMappingManager获取Handler.....这里的代码比较简单,一看就能明白。BundleHandlerMappingManager只是一个Map的简单操作,代码省略
publicclassOsgiDispatcherServletextendsDispatcherServletimplements
HandlerRegister
{
privatestaticfinalLog log=LogFactory
.getLog(OsgiDispatcherServlet.class);
/**//*HandlerMapping管理对象*/
privateBundleHandlerMappingManager bundleHandlerMappingManager;
privateBundleContext bundleContext;
publicOsgiDispatcherServlet(BundleContext bundleContext)
{
this.bundleContext=bundleContext;
this.bundleHandlerMappingManager=newBundleHandlerMappingManager();
}
protectedWebApplicationContext createWebApplicationContext(
WebApplicationContext parent)throwsBeansException
{
ClassLoader contextClassLoader=Thread.currentThread()
.getContextClassLoader();
try
{
ClassLoader cl=BundleDelegatingClassLoader
.createBundleClassLoaderFor(bundleContext.getBundle(),
getClass().getClassLoader());
Thread.currentThread().setContextClassLoader(cl);
LocalBundleContext.setContext(bundleContext);
ConfigurableWebApplicationContext wac=newOSGiXmlWebApplicationContext(
bundleContext);
wac.setParent(parent);
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
if(getContextConfigLocation()!=null)
{
wac
.setConfigLocations(StringUtils
.tokenizeToStringArray(
getContextConfigLocation(),
ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
wac.addApplicationListener(this);
wac.refresh();
returnwac;
}finally
{
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
/** *//**
* 重写这个方法是很有必要的
*/
protectedHandlerExecutionChain getHandler(HttpServletRequest request,
booleancache)throwsException
{
HandlerExecutionChain handler=(HandlerExecutionChain) request
.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if(handler!=null)
{
if(!cache)
{
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
returnhandler;
}
for(Iterator _it=this.bundleHandlerMappingManager
.getBundlesHandlerMapping().values().iterator(); _it.hasNext();)
{
List _handlerMappings=(List) _it.next();
for(Iterator it=_handlerMappings.iterator(); it.hasNext();)
{
HandlerMapping hm=(HandlerMapping) it.next();
if(logger.isDebugEnabled())
{
logger.debug("Testing handler map ["+hm
+"] in OsgiDispatcherServlet with name '"
+getServletName()+"'");
}
handler=hm.getHandler(request);
if(handler!=null)
{
if(cache)
{
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE,
handler);
}
returnhandler;
}
}
}
returnnull;
}
/** *//**
* 这个功能实现起来有点牵强,但是以演示为主,一笑而过
*/
protectedView resolveViewName(String viewName, Map model, Locale locale,
HttpServletRequest request)throwsException
{
longbundleId=this.bundleHandlerMappingManager.getBundleId(request);
Bundle bundle=this.bundleContext.getBundle(bundleId);
ViewResolver viewResolver=newOsgiInternalResourceViewResolver(
bundle, getWebApplicationContext(), viewName);
View view=viewResolver.resolveViewName(viewName, locale);
returnview;
}
publicvoidregisterHandler(ApplicationContext context, Bundle bundle)
{
Map matchingBeans=BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class,true,false);
if(!matchingBeans.isEmpty())
{
List _list=newArrayList(matchingBeans.values());
String bundleId=newLong(bundle.getBundleId()).toString();
this.bundleHandlerMappingManager.registerHandlerMapping(bundleId,
_list);
}
}
publicvoidunRegisterHandler(Bundle bundle)
{
String bundleId=newLong(bundle.getBundleId()).toString();
this.bundleHandlerMappingManager.unRegisterHandlerMapping(bundleId);
}
}
扩展InternalResourceViewResolver - org.phrancol.osgi.demo.mvc.springmvc.core.OsgiInternalResourceViewResolver
为了方便,这部份的代码写得有些不地道(演示为主~),重写getPrefix()方法,主要是为了获取jsp文件
publicclassOsgiInternalResourceViewResolverextends
InternalResourceViewResolver
{
privatestaticfinalLog log=LogFactory.getLog(OsgiInternalResourceViewResolver.class);
privatestaticfinalString PREFIX="/web/jsp/spring/";
privatestaticfinalString SUFFIX=".jsp";
privateString viewName;
privateBundle bundle;
publicOsgiInternalResourceViewResolver(Bundle bundle, ApplicationContext applicationContext , String viewName)
{
this.bundle=bundle;
setPrefix(PREFIX);
setSuffix(SUFFIX);
setViewClass(newJstlView().getClass());
setApplicationContext(applicationContext);
this.bundle=bundle;
this.viewName=viewName;
}
protectedString getPrefix()
{
String _prefix="/"+bundle.getSymbolicName()+PREFIX;
return_prefix;
}
}
MVCBundle需要设置一个Activator,用于将OsgiDispatcherServlet注册为OSGi Service
publicvoidstart(BundleContext bundleContext)throwsException
{
DispatcherServlet ds=newOsgiDispatcherServlet(bundleContext);
bundleContext.registerService(DispatcherServlet.class.getName(), ds,
null);
}
MVCBundle中的SpringmvcHttpServiceRegister还是需要的,它需要生成一个所谓的容器Context
publicclassSpringmvcHttpServiceRegisterimplementsHttpServiceRegister
{
publicvoidserviceRegister(BundleContext context,
ApplicationContext bundleApplicationContext)
{
try
{
ServiceReference sr=context.getServiceReference(HttpService.class
.getName());
HttpService httpService=(HttpService) context.getService(sr);
HttpContext defaultContext=httpService.createDefaultHttpContext();
Dictionaryinitparams=newHashtable();
initparams.put("load-on-startup","1");
/** *//**/
ContextLoaderServlet contextloaderListener = new BundleContextLoaderServlet(
context, bundleApplicationContext);
httpService.registerServlet("/initContext", contextloaderListener,
initparams, defaultContext);
/**/
DispatcherServlet dispatcherServlet=(DispatcherServlet) context
.getService(context
.getServiceReference(DispatcherServlet.class
.getName()));
/**//*这里给了 DispatcherServlet 一个空的配置文件,可以节省好多代码*/
dispatcherServlet
.setContextConfigLocation("META-INF/dispatcher/DynamicModule-servlet.xml");
initparams=newHashtable();
initparams.put("servlet-name","DynamicModule");
initparams.put("load-on-startup","2");
httpService.registerServlet("/*.do", dispatcherServlet, initparams,
defaultContext);
}catch(Exception e)
{
e.printStackTrace(System.out);
}
}
}
通过以上工作,Spring MVC就被简单的改造完了......当然他仅仅只是能实现我所要演示的功能
五、模块
新建一个模块bundle - org.phrancol.osgi.demo.mvc.springmvc.module2 ,Bundle-SymbolicName设置为module2
先看看它的bean配置
class="org.phrancol.osgi.demo.mvc.util.BundleServiceRegister">
class="org.phrancol.osgi.demo.mvc.springmvc.module2.SpringmvcHttpServiceRegister"/>
![4f1150b881333f12a311ae9ef34da474.png](https://i-blog.csdnimg.cn/blog_migrate/b2459b5195c09ae0fd014c54f2df42a3.png)
![4f1150b881333f12a311ae9ef34da474.png](https://i-blog.csdnimg.cn/blog_migrate/b2459b5195c09ae0fd014c54f2df42a3.png)
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
class="org.phrancol.osgi.demo.mvc.springmvc.module2.TheSecondModuleController">
也使用了一个SpringmvcHttpServiceRegister,它就是用来注册这个bundle 中的jsp和资源的
publicclassSpringmvcHttpServiceRegisterimplementsHttpServiceRegister
{
publicvoidserviceRegister(BundleContext context,
ApplicationContext bundleApplicationContext)
{
try
{
ServiceReference sr=context.getServiceReference(HttpService.class
.getName());
/**//*在上一个例子中,HttpContext的用法不对,这个用法才是正确的*/
HttpService httpService=(HttpService) context.getService(sr);
HttpContext defaultContext=httpService.createDefaultHttpContext();
httpService.registerResources("/module2","/module2",
defaultContext);
/**//*
* 这个JspServlet对象中的参数"module2/web",可以理解为 The root path of module
* application,它是干什么用的,请参考它的JavaDoc,建议从Eclipse的CVS中准备一份Equinox的源代码
*/
JspServlet jspServlet=newJspServlet(context.getBundle(),
"/module2/web");
httpService.registerServlet("/module2/*.jsp", jspServlet,null,
defaultContext);
HandlerRegister dispatcherServlet=(HandlerRegister) context
.getService(context
.getServiceReference(DispatcherServlet.class
.getName()));
dispatcherServlet.registerHandler(bundleApplicationContext, context
.getBundle());
}catch(Exception e)
{
e.printStackTrace(System.out);
}
}
}
来看看org.phrancol.osgi.demo.mvc.springmvc.module2.TheSecondModuleController ,只有很简单的一个输出
publicclassTheSecondModuleControllerimplementsController
{
privatestaticfinalString VIEWSTRING="Hello, this is the second module !";
publicModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)throwsException
{
Map model=newHashMap();
model.put("viewString", VIEWSTRING);
ModelAndView mv=newModelAndView("Success", model);
returnmv;
}
}
目录结构也有一点变化 /module1/web/jsp/spring/ *.jsp
模块1和模块2是一样的
六、运行
将模块二导出为bundle jar包,放到C盘根目录下,启动这个应用(当然不要启动modure2),在浏览器看看module1的运行情况
现在安装一下module2
试着访问一下module2
404,正常,启动一下这个bundle再看看
显示出来了,现在可以动态的操作这2个模块了......
七、扩展
通过这个演示,可以领略到OSGi带给我们的一小部分功能,做一些扩展看看
1. 当然是各种框架的支持。
2. 强大的bundle资源库
3. 绝对动态的部署框架,可以通过UI界面来操作。
4. 可以从URL来安装bundle, install http://www.domain.com/sampleBundle.jar ,如果是这样的,服务网关就能体现出来了,你提供一个服务框架,别人可以通过你的框架运行自己的服务。
5. 个人猜测,它将取代Portal的运行模式
6. ..........
八、结束语
OSGi在Web应用中还有很长的路要走,它到底会发展成什么样子,就目前的功能还真不好推测。
现在不管是MVC还是持久层都还没有框架对OSGi的支持,我个人准备用业余时间研究一下这方面,顺便也可以练练手,希望传说中的强人能开发这样的框架并不吝开源~
九、相关资源
就我目前能找到的一些资源,列出如下:
Struts2有一个OSGi的插件,但是我看了看,并不能达到预期效果,不过可以看一看
http://cwiki.apache.org/S2PLUGINS/osgi-plugin.html
在持久层方面,db4o似乎有这个打算,不做评论
http://www.db4o.com/osgi/
另外它的合作伙伴prosyst已经开发出了一个基于Equinox的OSGi Server,还有个专业版,好像要收费,所以也就没下载,不知道是个什么样子。
http://www.prosyst.com/
posted on 2007-11-01 15:09 Phrancol Yang 阅读(6123) 评论(5) 编辑 收藏 所属分类: OSGI