SpringDM把一个WAR作为一个Bundle, 其实是将对WAR的处理交给当前OSGi环境中的WEB容器去处理的,即:
WEB Application的创建和线程的管理委托给了WEB容器。 那么SpringDMV是如何决定一个Bundle是要委托给
WEB容得的呢?实现这个功能主要是通过Spring的:Extender 机制:
如果一个安装的Bundle是以.war作为扩展名的,他将自动触发SpringDM's Web extender将对该Bundle的处
理委托给underlying的WEB 容器。
1.Structure of Spring DM web OSGi components
SpringDM Web Extender 部署bundle至内嵌的Web容器需要的条件:
■ The bundle location (filename) ends with .war
■ The bundle contains a WEB-INF directory
目录结构:
my-web-bundle
META-INF/
MANIFEST.MF
WEB-INF/
web.xml
When a bundle is detected as a web bundle, the web extender will trigger its deployment
to the web container, letting the latter handle everything.
Bundle Context Path :
(1) SpringDM Web Bundle算法计算:
The default behavior of the Spring DM web extender, with respect to the definition of context
paths, is dictated by a context path strategy, the default being the DefaultContextPathStrategy
class (in the org.springframework.osgi.web.deployer.support package). This default strategy
bases its decision first on the presence of the Web-ContextPath header, and then, if the header
isn’t present, on the location of the bundle, by removing the trailing .war or .jar (just like in our
example). After that it falls back on some of the other bundle metadata (name, symbolic name, or
identity) if it can’t use the bundle location.
(2)明确定义:
Web-ContextPath: mycontext
2. Classloading in web bundles
2.1 Traditional,Standerd Web Application ClassPath's Structure
■ WEB-INF/classes directory—Contains application classes (web controllers, such as servlets).
They’re under their .class file form (not packaged) and organized following the traditional
tree-like structure of Java packages.
■ WEB-INF/lib directory—Contains third-party libraries, packaged as JAR files.
2.2 OSGi Web Bundle Class Loading
When you deploy a web application using the Spring DM web extender,OSGi-specific classloading
mechanisms take precedence. Remember that before being a web application, your application
was also an OSGi bundle; as such, its classpath is the OSGi classpath. It benefits from everything
OSGi provides for handling classloading (class visibility, versioning, reloading, and so on).
(1)The web application will be able to load classes from the following locations:
■ Its bundle space (classes in the JAR or WAR and classes from all the associated bundle
fragments)
■ Classes from the packages imported with the Import-Package header
■ All the exported classes from bundles appearing in the Require-Bundle header
(2)实现将SpringDM Web Bundle做成标准的WAR:
The Bundle-Classpath header, which indicates to the OSGi platform where to find classes and
resources in the bundle. This header defaults to ".", meaning the root of the bundle. You can
override this default value and set it to the traditional locations of classes and libraries in WARs
(you should not forget to include the default location):
Bundle-Classpath: .,WEB-INF/classes,WEB-INF/lib/libA.jar,WEB-INF/lib/libB.jar
3. OSGi-aware Spring web container
WAR Bundle 如何访问其他Bundle中的类?
SpringDM总是努力尝试去创建一OSGi上下文:OgiBundleXmlApplicationContext,这个Context包括
Spring Artifacts, 以及注册、消费的服务。而在WAR Bundle中,线程的管理被委托给了WEB容器,如果这个
Context不明确指定对其他Bundel而言是不可见的。
SpringDM解决这个问题是通过一个特殊的Context:OsgiBundleXmlWebApplicationContext,该Context
与某个Bundel的Context关联,而该Context常常配置在web.xml文件中。见下面配置。
In standard Spring-based web applications, the Spring container is started and stopped by the
web container itself, usually through a dedicated servlet listener, the ContextLoaderListener.
The ContextLoaderListener is part of the web module of the Spring Framework. You declare it in
the web.xml file of your web application, and in it you can set the location of your Spring
configuration files and, more importantly in our case, the implementation of ApplicationContext
you want to use. Spring DM comes with a specific application context implementation,
OsgiBundleXmlWebApplicationContext , which can communicate with the OSGi platform even
though its instances are created by a web container.
Configuring a web application to use the OSGi web application context:
<web-app (...)>
(...)
<context-param>
<!-- ContextLoaderListener 根据该参数判定是哪一种类型的Web应用上下文,这个参数默认是:
XmlWebApplicationContext,现在改成SOGi应用上下文,这样WAR Bundle则可以访问其它Bundle.
-->
<param-name>contextClass</param-name>
<param-value>
org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
</param-value>
</contxt-param>
<context-param>
<!--- 这个值默认为WEB-INF/applicationContext.xml,现在为自定义--->
<param-name>contextConfigLocation</param-name>
<param-value>
WEB-INF/applicationContext.xml,
WEB-INF/applicationContext-osgi.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>PBWDP</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.osgi.web.context.support
.OsgiBundleXmlWebApplicationContext
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>PBWDP</servlet-name>
<url-pattern>/APP/*</url-pattern>
</servlet-mapping>
(...)
</web-app>
注意:If you use the ContextLoaderListener and the OsgiBundleXmlWebApplicationContext in
your web applications, you must include their respective packages in the Import-Package header
of your web bundle manifest.
4. Spring DM web deployer
SpringDM提供了一个用于指定Web上下文发布路径的Header:Web-ContextPath ,默认下该值为:
http://localhost:8080/BundleName/
可自己定制该值,如:
Web-ContextPath: HelloWorld
访问路径为:http://localhost:8080/的HelloWorld/
The Spring DM web extender is implemented as a synchronous bundle listener,which is registered
by the activator of the bundle. The bundle listener doesn’t itself handle the deployment of web
bundles; it delegates this perilous task to a WAR deployer.(TomcatWarDeplouer,JettyWarDeployer)
Even though the activator of the web extender bundle coordinates and performs some of the
work (scanning of incoming bundles, thread management for deployment and undeployment), the
WAR deployer is in charge of truly deploying (or undeploying) web applications to the embedded
web container. Through the WarDeployer interface, Spring DM abstracts the action of deploying
and undeploying WAR files, allowing a separation of concerns,because WarDeployer
implementations just handle the deployment process for their web container, whereas the web
extender sticks to its primary goal: listening for bundles starting or stopping.
Spring DM provides two implementations of WarDeployer: one for Apache Tomcat (the default)
and one for Jetty .
WAR deployers interact directly with the underlying web container instance when the web
extender hands them a WAR bundle. They can manage their own instance of the web container,
but the usual way is to look it up in the OSGi service registry. By “instance of web container,”
we mean an instance of the core class of the web container—org.apache.catalina.Service for
Tomcat and org.mortbay.jetty.Server for Jetty.
5. 切换不同的Web容器
默认下,SpringDM将WAR委托给一个Tomcat实例,要切换成其他的Web容器,需要spring-osig-web
-extendr.jar, 当该JAR启动时,它将是这定位一个Tomcat实例,如果找不带将产生下面的异常:
Exception in thread "WebExtender-Init"`java.lang.NoClassDefFoundError:
org/apache/CatalinaLoader
如果找到一个Tomcat实例并且所有的依赖被Resolved,SpringDM将试着获取已经注册了得一个服务:
org.apache.catalina.Service, 如果找不到该服务,将产生下面异常:
org.springframework.osgi.OsgiException: Cannot create Tomcat deployer
根据上述理解,SpingDM使用不同的Web容器时需要考虑下面两个问题:
(1) Override SpringDM默认的extender value,而使用另一个Web容器;
(2) 由一个服务注册Bundle去启动OSGi Web 容器;
解决第一个问题可以使用OSGi 的Fragment来实现, 而解决第二个问题需要新开发一个启动Web容器的Bundle,
Tomcat和Jetty容器依赖于自己的配置文件(server.xml、jetty.xml)来制定Web容器的端口,SSL证书等信息;
因此该Bundle需包括启动Web容器的配置文件及将该Web容器注册为服务的一系列功能。 开发该Bundel可参考
Tomcat与jetty的实现:catalina.start.osgi-1.0.0.jar和jetty.start.osgi-1.0.0.jar。