转载请保留作者信息:
作者:88250
Blog:http:/blog.csdn.net/DL88250
MSN & Gmail & QQ:DL88250@gmail.com
摘要
上一次,我们分析了KF(Knopflerfish)框架的设计,实践了基于OSGi的应用程序框架的设计。这一讲,我们将基于KF实现的OSGi规范Http Service,做一个简单的Servlet结合HTML/Javascript的实践。关于基于OSGi的Web开发
在开始前,我们先看一下一些关于OSGi在Web应用方面的尝试。开发Web应用,最重要的要属Web Server了(亦或Application Server)。在Server的厂商中 IBM、BEA和JBoss应用服务器采用OSGi,虽然目前我们的主要任务的做应用,但是可以了解这些最基本的东西。
好了,简要说一下OSGi在Web上的开发方式:
1. 将Web Server作为Bundle插入OSGi
目前,Apache Jetty可以做到。使用Spring DM(Spring-OSGi)的话,Tomcat的OSGi整合在未来应该也可以做。参考 这个2. 在Web Server内使用OSGi的Bundle
在Web容器里使用Spring DM(Spring-OSGi)可以做到。目前来说
在Java的Web开发上,Spring Framework是首选,但是如果应用是以OSGi作为底层的话,情况就不同了:1. 使用Spring,从Web应用开发的角度来说,视图/持久化/事务管理方便,可结合其他框架或技术的选择余地大
2. 使用OSGi,从系统整体开发角度来说,扩展性、维护性超好,系统整体结构清晰,可以把商业/控制逻辑做到很优良的组件话
3. 结合这两者使用的话(Spring DM),支持的扩展有限且技术相当复杂,得不偿失
所以,权衡后,我更偏向与直接使用OSGi来做Web应用,因为现在手头的一个项目只是运用例简单的HTML,AJAX与Servlet。
让我们简单实践一下!
准备
同上一次 :-)开工:
1. 创建工程
打开NB6,创建一个普通Java应用工程——OSGiServlet:注意,www文件夹是存放HTML源文件的地方,要直接放在src文件夹下。然后要导入3个Jar libs:
http_all-2.0.0.jar: OSGi 的 http service实现
knopflerfish-osgi.jar: 基本OSGi实现
servlet-api.jar: Sun的servlet实现
2.编写Activator与Servlet类
/*
* @(#)Activator.java
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package cn.edu.ynu.sei.osgiservlet;
import java.net.URL;
import java.util.Hashtable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.knopflerfish.service.log.LogRef;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
/**
* OSGi activator for <code>InfoServlet</code>.
* @author 88250
* @version 1.0.0.0, Mar 5, 2008
*/
public class Activator implements BundleActivator {
// This is my world
static BundleContext bc;
static LogRef log;
static final String RES_ALIAS = "/"; // the http server root
static final String RES_DIR = "/www"; // bundle resource directory
static final String SERVLET_ALIAS = "/servlet/firstservlet"; // a small servlet
Hashtable registrations = new Hashtable();
public void start(BundleContext bc) throws BundleException {
this.bc = bc;
this.log = new LogRef(bc);
ServiceListener listener = new ServiceListener() {
public void serviceChanged(ServiceEvent ev) {
ServiceReference sr = ev.getServiceReference();
switch (ev.getType()) {
case ServiceEvent.REGISTERED:
setRoot(sr);
break;
case ServiceEvent.UNREGISTERING:
unsetRoot(sr);
break;
}
}
};
String filter = "(objectclass=" + HttpService.class.getName() + ")";
try {
bc.addServiceListener(listener, filter);
ServiceReference[] srl = bc.getServiceReferences(null, filter);
for (int i = 0; srl != null && i < srl.length; i++) {
listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
srl[i]));
}
} catch (Exception e) {
log.error("Failed to set up listener for http service", e);
}
}
public void stop(BundleContext bc) throws BundleException {
}
void setRoot(ServiceReference sr) {
if (registrations.containsKey(sr)) {
return; // already done
}
log.info("set root for " + sr);
HttpService http = (HttpService) bc.getService(sr);
HttpContext context = new HttpContext() {
public boolean handleSecurity(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException {
return true;
}
public URL getResource(String name) {
// when registering the root, it seems
// like we get no separator before the file.
// Is that a bug?? Code below is a workaround
if (name.startsWith(RES_DIR) &&
name.length() > RES_DIR.length() &&
'/' != name.charAt(RES_DIR.length())) {
name = RES_DIR + "/" + name.substring(RES_DIR.length());
}
// default to index.html
if (name.equals(RES_DIR)) {
name = "/www/index.html";
}
// and send the plain file
URL url = getClass().getResource(name);
return url;
}
public String getMimeType(String reqEntry) {
return null; // server decides type
}
};
try {
http.registerResources(RES_ALIAS, RES_DIR, context);
http.registerServlet(SERVLET_ALIAS, new InfoServlet(sr),
new Hashtable(), context);
registrations.put(sr, context);
} catch (Exception e) {
log.error("Failed to register resource", e);
}
}
void unsetRoot(ServiceReference sr) {
if (!registrations.containsKey(sr)) {
return; // nothing to do
}
log.info("unset root for " + sr);
HttpService http = (HttpService) bc.getService(sr);
if (http != null) {
http.unregister(RES_ALIAS);
http.unregister(SERVLET_ALIAS);
bc.ungetService(sr);
}
registrations.remove(sr);
}
}
/*
* @(#)Activator.java
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package cn.edu.ynu.sei.osgiservlet;
import java.net.URL;
import java.util.Hashtable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.knopflerfish.service.log.LogRef;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
/**
* OSGi activator for <code>InfoServlet</code>.
* @author 88250
* @version 1.0.0.0, Mar 5, 2008
*/
public class Activator implements BundleActivator {
// This is my world
static BundleContext bc;
static LogRef log;
static final String RES_ALIAS = "/"; // the http server root
static final String RES_DIR = "/www"; // bundle resource directory
static final String SERVLET_ALIAS = "/servlet/firstservlet"; // a small servlet
Hashtable registrations = new Hashtable();
public void start(BundleContext bc) throws BundleException {
this.bc = bc;
this.log = new LogRef(bc);
ServiceListener listener = new ServiceListener() {
public void serviceChanged(ServiceEvent ev) {
ServiceReference sr = ev.getServiceReference();
switch (ev.getType()) {
case ServiceEvent.REGISTERED:
setRoot(sr);
break;
case ServiceEvent.UNREGISTERING:
unsetRoot(sr);
break;
}
}
};
String filter = "(objectclass=" + HttpService.class.getName() + ")";
try {
bc.addServiceListener(listener, filter);
ServiceReference[] srl = bc.getServiceReferences(null, filter);
for (int i = 0; srl != null && i < srl.length; i++) {
listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
srl[i]));
}
} catch (Exception e) {
log.error("Failed to set up listener for http service", e);
}
}
public void stop(BundleContext bc) throws BundleException {
}
void setRoot(ServiceReference sr) {
if (registrations.containsKey(sr)) {
return; // already done
}
log.info("set root for " + sr);
HttpService http = (HttpService) bc.getService(sr);
HttpContext context = new HttpContext() {
public boolean handleSecurity(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException {
return true;
}
public URL getResource(String name) {
// when registering the root, it seems
// like we get no separator before the file.
// Is that a bug?? Code below is a workaround
if (name.startsWith(RES_DIR) &&
name.length() > RES_DIR.length() &&
'/' != name.charAt(RES_DIR.length())) {
name = RES_DIR + "/" + name.substring(RES_DIR.length());
}
// default to index.html
if (name.equals(RES_DIR)) {
name = "/www/index.html";
}
// and send the plain file
URL url = getClass().getResource(name);
return url;
}
public String getMimeType(String reqEntry) {
return null; // server decides type
}
};
try {
http.registerResources(RES_ALIAS, RES_DIR, context);
http.registerServlet(SERVLET_ALIAS, new InfoServlet(sr),
new Hashtable(), context);
registrations.put(sr, context);
} catch (Exception e) {
log.error("Failed to register resource", e);
}
}
void unsetRoot(ServiceReference sr) {
if (!registrations.containsKey(sr)) {
return; // nothing to do
}
log.info("unset root for " + sr);
HttpService http = (HttpService) bc.getService(sr);
if (http != null) {
http.unregister(RES_ALIAS);
http.unregister(SERVLET_ALIAS);
bc.ungetService(sr);
}
registrations.remove(sr);
}
}
* @(#)InfoServlet.java
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package cn.edu.ynu.sei.osgiservlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.osgi.framework.ServiceReference;
/**
* The first OSGi servlet application.
* @author 88250
* @version 1.0.0.0, Mar 5, 2008
*/
public class InfoServlet extends HttpServlet {
ServiceReference httpSR;
public InfoServlet(ServiceReference httpSR) {
this.httpSR = httpSR;
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,
IOException {
// Handle just as GET
doGet(request, response);
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
response.setContentType("text/html");
out.println("<html>");
out.println("<head>");
out.println("<title>Demo</title>");
out.println("I am a simple servlet base running in OSGi :-)");
out.println("</head>");
out.println("<body>");
try {
} catch (Exception e) {
out.println("<pre>");
e.printStackTrace(out);
out.println("</pre>");
}
}
}
还有一个简单的HTML页面(/www/index.html):
<head>
<title>The First OSGi Servlet Appication</title>
</head>
<body>
<h2>Test page for OSGi web server</h2>
<p>
Hello OSGi Servlet
</p>
<script type="text/javascript">
document. writeln("Test javascript!");
</script>
</body>
</html>
3. manifest.mf
Bundle-ManifestVersion: 2
Bundle-Name: OSGi Servlet
Bundle-SymbolicName: cn.edu.ynu.sei.osgiservlet
Bundle-Version: 1.0.0.0
Bundle-Category: example
Bundle-Description: Demo HTTP servlet
Bundle-Activator: cn.edu.ynu.sei.osgiservlet.Activator
Import-Package: javax.servlet,
javax.servlet.http,
org.knopflerfish.service.log,
org.osgi.framework,
org.osgi.service.http
4. 测试
编译这个Bundle,安装它到KF里并运行之,结果如下图:以地址 http://localhost:8080/servlet/firstservlet 访问我们的Servlet:
总结
本次,我们基于OSGi做了一个简单的HTTP/Servlet 容器,可以满足简单的应用。这样,我们可以把我们的应用统一在一个框架里,简化了很多技术细节。但是,做企业应用的话这样是远远不够的,我们也不是太有可能基于OSGi构建一个自己的Application Server,所以,只要容器支持OSGi,结合Spring DM是最好的选择。