在前一篇文章中,通过OSGi(Open Services Gateway Initiative)的热插拔特性,我们已经窥见了OSGi在Java Web开发中的一些优势。但是因为OSGi是最早为嵌入式系统设计,所以OSGi标准中的HTTP服务只提供了有限的Servlet与静态资源的发布功能,没有一个完整的WEB容器概念,这种模式更适合通过WEB方式暴露(Export)服务,不太适合开发展现层的WEB应用。这也是导致很多公司很少涉足这个领域的很大因素。在这份文档中,用一个简单的例子来说明如何在使用JSP以及使用JSTL等标签库的情况下应用OSGi开发基于组建的Web应用。
1. 创建plug-in工程osgi_jsp_test
2. 在工程的根目录下创建"web"目录,并在其中增加WEB-INF文件夹。
3. 建立web.xml文件,内容如下:
- <?xml version="1.0" encoding="ISO-8859-1"?>
- <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
- version="2.4">
- <description>JSP 2.0 Examples.</description>
- <display-name>JSP 2.0 Examples</display-name>
- </web-app>
4.编写JSP,内容如下
foreach.jsp
- <html>
- <head>
- <title>Tag Plugin Examples: forEach</title>
- </head>
- <body>
- <!-- 例子一 -->
- <!-- forEach标签示例 -->
- <h1>Tag Plugin Examples - <c:forEach></h1>
- <hr><font color="#000000"/></br>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <%@ page import="java.util.Vector" %>
- <h3>Iterating over a range</h3>
- <c:forEach var="item" begin="1" end="100">
- ${item}
- </c:forEach>
- <%
- Vector v = new Vector();
- v.add("One");
- v.add("Two");
- v.add("Three");
- v.add("Four");
- pageContext.setAttribute("vector", v);
- %>
- <h3>Iterating over a Vector</h3>
- <c:forEach items="${vector}" var="item" >
- ${item}
- </c:forEach>
- <!-- 例子二 -->
- <!-- c:if标签示例 -->
- <h1>Tag Plugin Examples - <c:if></h1><hr>
- <font color="#000000"/>
- </br>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <h3>Set the test result to a variable</h3>
- <c:if test="${1==1}" var="theTruth" scope="session"/>
- The result of testing for (1==1) is: ${theTruth}
- <h3>Conditionally execute the body</h3>
- <c:if test="${2>0}">
- It's true that (2>0)!
- </c:if>
- <!-- 例子三 -->
- <!-- choose示例 -->
- <h1>Tag Plugin Examples - <c:choose></h1>
- <hr></br>
- <font color="#000000"/></br>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <c:forEach var="index" begin="0" end="4"># ${index}:
- <c:choose>
- <c:when test="${index == 1}">One!</br></c:when>
- <c:when test="${index == 4}">Four!</br></c:when>
- <c:when test="${index == 3}">Three!</br></c:when>
- <c:otherwise>Danlley?</br></c:otherwise>
- </c:choose>
- </c:forEach>
- </body>
- </html>
<html>
<head>
<title>Tag Plugin Examples: forEach</title>
</head>
<body>
<!-- 例子一 -->
<!-- forEach标签示例 -->
<h1>Tag Plugin Examples - <c:forEach></h1>
<hr><font color="#000000"/></br>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.util.Vector" %>
<h3>Iterating over a range</h3>
<c:forEach var="item" begin="1" end="100">
${item}
</c:forEach>
<%
Vector v = new Vector();
v.add("One");
v.add("Two");
v.add("Three");
v.add("Four");
pageContext.setAttribute("vector", v);
%>
<h3>Iterating over a Vector</h3>
<c:forEach items="${vector}" var="item" >
${item}
</c:forEach>
<!-- 例子二 -->
<!-- c:if标签示例 -->
<h1>Tag Plugin Examples - <c:if></h1><hr>
<font color="#000000"/>
</br>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h3>Set the test result to a variable</h3>
<c:if test="${1==1}" var="theTruth" scope="session"/>
The result of testing for (1==1) is: ${theTruth}
<h3>Conditionally execute the body</h3>
<c:if test="${2>0}">
It's true that (2>0)!
</c:if>
<!-- 例子三 -->
<!-- choose示例 -->
<h1>Tag Plugin Examples - <c:choose></h1>
<hr></br>
<font color="#000000"/></br>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:forEach var="index" begin="0" end="4"># ${index}:
<c:choose>
<c:when test="${index == 1}">One!</br></c:when>
<c:when test="${index == 4}">Four!</br></c:when>
<c:when test="${index == 3}">Three!</br></c:when>
<c:otherwise>Danlley?</br></c:otherwise>
</c:choose>
</c:forEach>
</body>
</html>
5. 编辑配置文件
---------------------------------------------------
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Osgi_jsp_test Plug-in
Bundle-SymbolicName: osgi_jsp_test
Bundle-Version: 1.0.0
Bundle-ClassPath: target/classes/,
lib/jstl.jar,
lib/standard.jar
Bundle-Activator: org.danlley.osgi.jsp.common.Activator
Import-Package: org.osgi.framework;version="1.3.0"
Require-Bundle: javax.servlet,
javax.servlet.jsp,
org.mortbay.jetty,
org.apache.commons.logging,
org.apache.commons.el,
org.apache.jasper,
org.eclipse.equinox.http.registry,
org.eclipse.equinox.http.helper,
org.eclipse.equinox.http.jetty,
org.eclipse.equinox.http.servlet,
org.eclipse.equinox.jsp.jasper,
org.eclipse.equinox.jsp.jasper.registry,
org.eclipse.equinox.registry,
org.eclipse.equinox.common,
org.eclipse.equinox.app,
org.eclipse.osgi,
org.eclipse.osgi.util,
org.eclipse.osgi.services,
org.eclipse.core.commands,
org.eclipse.core.jobs
---------------------------------------------------
6. 修改Activator类
注,由于Bundle可以在任何时候安装,启动,停止或卸载,所以使用其他Bundle提供的服务时,必须跟踪这些服务的状态。如果在一个Bundle中需要使用其他Bundle提供的服务,则需要在考虑服务不存在或突然被注销的情形。由于OSGi环境是一个动态的环境,如果使用一个服务,可以使用ServiceTracker对该服务进行跟踪。我们在这里使用一下ServiceTracker,在OSGi提供的接口中,对ServiceTracker提供了四种对象初始化方式:
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceReference;
- import org.osgi.util.tracker.ServiceTrackerCustomizer;
- import org.osgi.framework.Filter;
- public ServiceTracker(BundleContext context,ServiceReference reference,ServiceTrackerCustomizer
- customizer);
- public ServiceTracker(BundleContext context,String clazz,ServiceTrackerCustomizer customizer);
- public ServiceTracker(BundleContext context,Filter filter,ServiceTrackerCustomizer customizer);
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.osgi.framework.Filter;
public ServiceTracker(BundleContext context,ServiceReference reference,ServiceTrackerCustomizer
customizer);
public ServiceTracker(BundleContext context,String clazz,ServiceTrackerCustomizer customizer);
public ServiceTracker(BundleContext context,Filter filter,ServiceTrackerCustomizer customizer);
BundleContext是一个OSGi框架与Bundle之间进行交互的一个结合点。当代码需要在任何时候与框架交互时,你将用到BundleContext。事实上这是用OSGi API交互的唯一方法,并且在框架启动Bundle的时候时,每个Bundle的Activator都会接收到BundleContext的实例。
OSGi中services总是通过ServiceReference来访问的,他唯一对应一个service对象要访问的一个服务,以下步骤是必须的:
1)。获取ServiceReference引用
2)。通过BundleContext.getService()获取服务对象实例
- final HttpService httpService = (HttpService) context.getService(reference);
final HttpService httpService = (HttpService) context.getService(reference);
ServiceTrackCustomer是由OSGi提供的一个用于监控服务的接口类,当在OSGi里面注册,修改,反注册一个Service的时候,OSGi会调用ServiceTrackCustomer的方法addingService, modifiedService, removedService. 关于这个方面的示例,我想应该可以在我的另外一篇文章《管窥Java开发中OSGi组件的热插拔》中找到:http://danlley.javaeye.com/blog/218676
接下来,实现Activator类,由于我们需要执行JSP页面,因此就需要在系统中加载JspServlet,为此,我们需要复写在类中实现的一个成员方法:
- public java.lang.Object addingService(org.osgi.framework.ServiceReference reference);
public java.lang.Object addingService(org.osgi.framework.ServiceReference reference);
实现如下:
- public Object addingService(ServiceReference reference) {
- final HttpService httpService = (HttpService) context.getService(reference);
- try {
- System.out.println("开始注册JspServlet");
- HttpContext commonContext = new BundleEntryHttpContext(context.getBundle(),
- "/web");
- httpService.registerResources("/jsp-examples", "/", commonContext);
- Servlet adaptedJspServlet = new ContextPathServletAdaptor(new
- JspServlet(context.getBundle(), "/web"), "/jsp-examples");
- httpService.registerServlet("/jsp-examples/*.jsp", adaptedJspServlet, null,
- commonContext);
- System.out.println("JspServlet注册结束");
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return httpService;
- }
public Object addingService(ServiceReference reference) {
final HttpService httpService = (HttpService) context.getService(reference);
try {
System.out.println("开始注册JspServlet");
HttpContext commonContext = new BundleEntryHttpContext(context.getBundle(),
"/web");
httpService.registerResources("/jsp-examples", "/", commonContext);
Servlet adaptedJspServlet = new ContextPathServletAdaptor(new
JspServlet(context.getBundle(), "/web"), "/jsp-examples");
httpService.registerServlet("/jsp-examples/*.jsp", adaptedJspServlet, null,
commonContext);
System.out.println("JspServlet注册结束");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return httpService;
}
HttpService 通过这样的方法来注册资源文件:HttpService.registerResources(资源 Mapping Url,资源文件路径, HttpContext 实例);
示例:service.registerResources("/demo/page","page",null);
构建HttpContext实例,org.eclipse.equinox.http.helper项目为我们已经做好了一个构建HttpContext实例的类,并提供了两个构造方法:
- public BundleEntryHttpContext(Bundle bundle);
- public BundleEntryHttpContext(Bundle b, String bundlePath);
public BundleEntryHttpContext(Bundle bundle);
public BundleEntryHttpContext(Bundle b, String bundlePath);
HttpContext接口的定义如下:
- public abstract interface org.osgi.service.http.HttpContext {
- public static final java.lang.String REMOTE_USER = "org.osgi.service.http.authentication.remote.user";
- public static final java.lang.String AUTHENTICATION_TYPE = "org.osgi.service.http.authentication.type";
- public static final java.lang.String AUTHORIZATION = "org.osgi.service.useradmin.authorization";
- public boolean handleSecurity(javax.servlet.http.HttpServletRequest arg0,
- javax.servlet.http.HttpServletResponse arg1)throws java.io.IOException;
- public java.net.URL getResource(java.lang.String arg0);
- public java.lang.String getMimeType(java.lang.String arg0);
- }
public abstract interface org.osgi.service.http.HttpContext {
public static final java.lang.String REMOTE_USER = "org.osgi.service.http.authentication.remote.user";
public static final java.lang.String AUTHENTICATION_TYPE = "org.osgi.service.http.authentication.type";
public static final java.lang.String AUTHORIZATION = "org.osgi.service.useradmin.authorization";
public boolean handleSecurity(javax.servlet.http.HttpServletRequest arg0,
javax.servlet.http.HttpServletResponse arg1)throws java.io.IOException;
public java.net.URL getResource(java.lang.String arg0);
public java.lang.String getMimeType(java.lang.String arg0);
}
在BundleEntryHttpContext对HttpContext接口的实现过程时,如果我们采用的是第二种方式(传入BundlePath),在调用getResource()方法时,resourceName就会加上BundlePath作为路径前缀。
适配器ContextPathServletAdaptor事实上是对javax.servlet.Servlet接口的一个实现。Servlet接口定义如下:
- public abstract interface javax.servlet.Servlet {
- public abstract void init(javax.servlet.ServletConfig arg0) throws
- javax.servlet.ServletException;
- public abstract javax.servlet.ServletConfig getServletConfig();
- public abstract void service(javax.servlet.ServletRequest arg0,
- javax.servlet.ServletResponse arg1) throws
- javax.servlet.ServletException, java.io.IOException;
- public abstract java.lang.String getServletInfo();
- public abstract void destroy();
- }
public abstract interface javax.servlet.Servlet {
public abstract void init(javax.servlet.ServletConfig arg0) throws
javax.servlet.ServletException;
public abstract javax.servlet.ServletConfig getServletConfig();
public abstract void service(javax.servlet.ServletRequest arg0,
javax.servlet.ServletResponse arg1) throws
javax.servlet.ServletException, java.io.IOException;
public abstract java.lang.String getServletInfo();
public abstract void destroy();
}
Eclipse的org.eclipse.equinox.http.servlet项目对OSGi的HttpService做了一个实现类HttpServiceImpl,通过他提供的registerServlet接口实现,我们就可以将JspServlet注册到我们的OSGi环境中。HttpServiceImpl的接口实现声明如下
- public class org.eclipse.equinox.http.servlet.internal.HttpServiceImpl implements org.osgi.service.http.HttpService {
- private org.osgi.framework.Bundle bundle;
- private org.eclipse.equinox.http.servlet.internal.ProxyServlet proxy;
- java.util.Set aliases;
- public HttpServiceImpl(org.osgi.framework.Bundle bundle,
- org.eclipse.equinox.http.servlet.internal.ProxyServlet proxy);
- public synchronized void unregisterAliases();
- public synchronized void registerServlet(java.lang.String alias, javax.servlet.Servlet servlet,
- java.util.Dictionary initparams, org.osgi.service.http.HttpContext context) throws
- javax.servlet.ServletException, org.osgi.service.http.NamespaceException;
- public synchronized void registerResources(java.lang.String alias, java.lang.String name,
- org.osgi.service.http.HttpContext context) throws org.osgi.service.http.NamespaceException;
- public synchronized void unregister(java.lang.String alias);
- public org.osgi.service.http.HttpContext createDefaultHttpContext();
- }
public class org.eclipse.equinox.http.servlet.internal.HttpServiceImpl implements org.osgi.service.http.HttpService {
private org.osgi.framework.Bundle bundle;
private org.eclipse.equinox.http.servlet.internal.ProxyServlet proxy;
java.util.Set aliases;
public HttpServiceImpl(org.osgi.framework.Bundle bundle,
org.eclipse.equinox.http.servlet.internal.ProxyServlet proxy);
public synchronized void unregisterAliases();
public synchronized void registerServlet(java.lang.String alias, javax.servlet.Servlet servlet,
java.util.Dictionary initparams, org.osgi.service.http.HttpContext context) throws
javax.servlet.ServletException, org.osgi.service.http.NamespaceException;
public synchronized void registerResources(java.lang.String alias, java.lang.String name,
org.osgi.service.http.HttpContext context) throws org.osgi.service.http.NamespaceException;
public synchronized void unregister(java.lang.String alias);
public org.osgi.service.http.HttpContext createDefaultHttpContext();
}
下面给出Activator类代码的完整实现,代码如下:
Activator.java
- package org.danlley.osgi.jsp.common;
- import javax.servlet.Servlet;
- import org.eclipse.equinox.http.helper.BundleEntryHttpContext;
- import org.eclipse.equinox.http.helper.ContextPathServletAdaptor;
- import org.eclipse.equinox.jsp.jasper.JspServlet;
- import org.osgi.framework.BundleActivator;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceReference;
- import org.osgi.service.http.HttpContext;
- import org.osgi.service.http.HttpService;
- import org.osgi.util.tracker.ServiceTracker;
- public class Activator implements BundleActivator {
- private ServiceTracker httpServiceTracker;
- public void start(BundleContext context) throws Exception {
- httpServiceTracker = new HttpServiceTracker(context);
- httpServiceTracker.open();
- }
- public void stop(BundleContext context) throws Exception {
- httpServiceTracker.open();
- }
- private class HttpServiceTracker extends ServiceTracker {
- public HttpServiceTracker(BundleContext context) {
- super(context, HttpService.class.getName(), null);
- }
- public Object addingService(ServiceReference reference) {
- final HttpService httpService = (HttpService) context.getService(reference);
- try {
- System.out.println("开始注册JspServlet");
- HttpContext commonContext = new BundleEntryHttpContext(context.getBundle(),
- "/web");
- httpService.registerResources("/jsp-examples", "/", commonContext);
- Servlet adaptedJspServlet = new ContextPathServletAdaptor(new
- JspServlet(context.getBundle(), "/web"), "/jsp-examples");
- httpService.registerServlet("/jsp-examples/*.jsp", adaptedJspServlet, null,
- commonContext);
- System.out.println("JspServlet注册结束");
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return httpService;
- }
- public void removedService(ServiceReference reference, Object service) {
- final HttpService httpService = (HttpService) service;
- httpService.unregister("/jsp-examples");
- httpService.unregister("/jsp-examples/*.jsp");
- super.removedService(reference, service);
- }
- }
- }
package org.danlley.osgi.jsp.common;
import javax.servlet.Servlet;
import org.eclipse.equinox.http.helper.BundleEntryHttpContext;
import org.eclipse.equinox.http.helper.ContextPathServletAdaptor;
import org.eclipse.equinox.jsp.jasper.JspServlet;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.util.tracker.ServiceTracker;
public class Activator implements BundleActivator {
private ServiceTracker httpServiceTracker;
public void start(BundleContext context) throws Exception {
httpServiceTracker = new HttpServiceTracker(context);
httpServiceTracker.open();
}
public void stop(BundleContext context) throws Exception {
httpServiceTracker.open();
}
private class HttpServiceTracker extends ServiceTracker {
public HttpServiceTracker(BundleContext context) {
super(context, HttpService.class.getName(), null);
}
public Object addingService(ServiceReference reference) {
final HttpService httpService = (HttpService) context.getService(reference);
try {
System.out.println("开始注册JspServlet");
HttpContext commonContext = new BundleEntryHttpContext(context.getBundle(),
"/web");
httpService.registerResources("/jsp-examples", "/", commonContext);
Servlet adaptedJspServlet = new ContextPathServletAdaptor(new
JspServlet(context.getBundle(), "/web"), "/jsp-examples");
httpService.registerServlet("/jsp-examples/*.jsp", adaptedJspServlet, null,
commonContext);
System.out.println("JspServlet注册结束");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return httpService;
}
public void removedService(ServiceReference reference, Object service) {
final HttpService httpService = (HttpService) service;
httpService.unregister("/jsp-examples");
httpService.unregister("/jsp-examples/*.jsp");
super.removedService(reference, service);
}
}
}
7.启动服务
----------------------------------------------------------------------------
osgi> Jul 25, 2008 4:51:00 PM org.mortbay.http.HttpServer doStart
INFO: Version Jetty/5.1.x
Jul 25, 2008 4:51:00 PM org.mortbay.util.Container start
INFO: Started org.mortbay.jetty.servlet.ServletHandler@10f6d3
Jul 25, 2008 4:51:00 PM org.mortbay.util.Container start
INFO: Started HttpContext[/,/]
Jul 25, 2008 4:51:00 PM org.mortbay.http.SocketListener start
INFO: Started SocketListener on 0.0.0.0:80
Jul 25, 2008 4:51:00 PM org.mortbay.util.Container start
INFO: Started org.mortbay.http.HttpServer@1adc30
开始注册JspServlet
JspServlet注册结束
ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE javax.servlet_2.4.0.v200706111738
3 ACTIVE org.eclipse.equinox.jsp.jasper.registry_1.0.0.v20070607
4 ACTIVE org.eclipse.core.jobs_3.3.1.R33x_v20070709
5 ACTIVE javax.servlet.jsp_2.0.0.v200706191603
6 ACTIVE org.apache.commons.logging_1.0.4
7 ACTIVE org.apache.commons.logging_1.0.4.v200706111724
8 ACTIVE org.eclipse.equinox.http.helper_1.0.0.qualifier
9 ACTIVE org.eclipse.osgi.services_3.1.200.v20070605
10 ACTIVE org.eclipse.equinox.jsp.jasper_1.0.100.qualifier
11 ACTIVE org.eclipse.core.commands_3.3.0.I20070605-0010
12 ACTIVE org.eclipse.equinox.http.registry_1.0.1.R33x_v20071231
13 ACTIVE org.apache.commons.el_1.0.0.v200706111724
14 ACTIVE org.eclipse.equinox.common_3.3.0.v20070426
15 ACTIVE org.eclipse.equinox.http.jetty_1.0.1.R33x_v20070816
16 ACTIVE org.eclipse.equinox.app_1.0.1.R33x_v20070828
17 ACTIVE org.mortbay.jetty_5.1.11.v200706111724
18 ACTIVE org.eclipse.equinox.http.servlet_1.0.1.R33x_v20070816
19 ACTIVE org.eclipse.equinox.registry_3.3.1.R33x_v20070802
21 ACTIVE org.apache.jasper_5.5.17.v200806031609
24 ACTIVE org.eclipse.osgi.util_3.1.200.v20070605
32 ACTIVE osgi_jsp_test_1.0.0
----------------------------------------------------------------------------