本章将创建一个Web Application项目,并描述如何在此应用中启动OSGi。
首先,在Eclipse中创建一个Dynamic Web Project,名字为OSGi-Web,Context root为osgi。
这个项目只作为部署Web Application使用,相关java代码放在另外一个Java Project中,因此我们再创建一个新的Java Project,名字为OSGi-Web-Launcher。然后在OSGi-Web项目的Java EE Module Dependencies中设置OSGi-Web-Launcher为关联,这样在部署的时候,OSGi-Web-Launcher项目中的java代码将为打包为jar存放到Web的WEB-INF/lib目录之中。
为了启动OSGi,我们在web中增加一个ServletContextListener监听器实现,并且通过这个监听器来控制OSGi容器的启动和终止。
在OSGi-Web-Launcher项目中增加一个java类,类名为FrameworkConfigListener,实现接口ServletContextListener,package为org.dbstar.osgi.web.launcher。在contextInitialized方法中,增加启动OSGi的代码,在contextDestroyed方法中,增加停止OSGi的代码,这样我们就可以使OSGi容器的生命周期与ServletContext的生命周期保持一致了。
启动OSGi容器:
感谢OSGi规范4.2给了我们一个简单统一的启动OSGi容器的方式,所有实现OSGi4.2规范的容器实力都应该实现这种启动方式,那就是通过org.osgi.framework.launch.FrameworkFactory,同时,还必须在其实现jar中放置一个文件:META-INF/services/org.osgi.framework.launch.FrameworkFactory,这个文件中设置了实际的FrameworkFactory实现类的类名。在equinox-SDK-3.6M5的org.eclipse.osgi_3.6.0.v20100128-1430.jar中,这个文件的内容是:org.eclipse.osgi.launch.EquinoxFactory。
我们先写一个工具类来载入这个配置文件中的内容:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
package
org.dbstar.osgi.web.launcher;
2
3
import
java.io.BufferedReader;
4
import
java.io.IOException;
5
import
java.io.InputStream;
6
import
java.io.InputStreamReader;
7
8
public
abstract
class
ServiceLoader {
9
public
final
static
<
E
>
Class
<
E
>
load(Class
<
E
>
clazz)
throws
IOException, ClassNotFoundException {
10
return
load(clazz, Thread.currentThread().getContextClassLoader());
11
}
12
13
@SuppressWarnings(
"
unchecked
"
)
14
public
final
static
<
E
>
Class
<
E
>
load(Class
<
E
>
clazz, ClassLoader classLoader)
throws
IOException,
15
ClassNotFoundException {
16
String resource
=
"
META-INF/services/
"
+
clazz.getName();
17
InputStream in
=
classLoader.getResourceAsStream(resource);
18
if
(in
==
null
)
return
null
;
19
20
try
{
21
BufferedReader reader
=
new
BufferedReader(
new
InputStreamReader(in));
22
String serviceClassName
=
reader.readLine();
23
return
(Class
<
E
>
) classLoader.loadClass(serviceClassName);
24
}
finally
{
25
in.close();
26
}
27
}
28
}
然后获取到FrameworkFactory的实例类:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
try
{
2
frameworkFactoryClass
=
ServiceLoader.load(FrameworkFactory.
class
);
3
}
catch
(Exception e) {
4
throw
new
IllegalArgumentException(
"
FrameworkFactory service load error.
"
, e);
5
}
6
if
(frameworkFactoryClass
==
null
) {
7
throw
new
IllegalArgumentException(
"
FrameworkFactory service not found.
"
);
8
}
实例化FrameworkFactory:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
FrameworkFactory frameworkFactory;
2
try
{
3
frameworkFactory
=
frameworkFactoryClass.newInstance();
4
}
catch
(Exception e) {
5
throw
new
IllegalArgumentException(
"
FrameworkFactory instantiation error.
"
, e);
6
}
获取Framework的启动配置:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
Map
<
Object, Object
>
configuration;
2
try
{
3
//
载入Framework启动配置
4
configuration
=
loadFrameworkConfig(event.getServletContext());
5
if
(logger.isInfoEnabled()) {
6
logger.info(
"
Load Framework configuration: [
"
);
7
for
(Object key : configuration.keySet()) {
8
logger.info(
"
\t
"
+
key
+
"
=
"
+
configuration.get(key));
9
}
10
logger.info(
"
]
"
);
11
}
12
}
catch
(Exception e) {
13
throw
new
IllegalArgumentException(
"
Load Framework configuration error.
"
, e);
14
}
启动配置读取外部配置文件,可以在此配置文件中增加OSGi容器实现类相关的配置项,例如Equinox的osgi.console:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
//
载入Framework启动配置
2
private
static
Map
<
Object, Object
>
loadFrameworkConfig(ServletContext context)
throws
MalformedURLException {
3
String configLocation
=
context.getInitParameter(CONTEXT_PARAM_OSGI_CONFIG_LOCATION);
4
if
(configLocation
==
null
) configLocation
=
DEFAULT_OSGI_CONFIG_LOCATION;
5
else
if
(
!
configLocation.startsWith(
"
/
"
)) configLocation
=
"
/
"
.concat(configLocation);
6
7
Properties config
=
new
Properties();
8
try
{
9
//
载入配置项
10
config.load(context.getResourceAsStream(configLocation));
11
if
(logger.isInfoEnabled()) logger.info(
"
Load Framework configuration from:
"
+
configLocation);
12
}
catch
(IOException e) {
13
if
(logger.isWarnEnabled()) logger.warn(
"
Load Framework configuration error from:
"
+
configLocation, e);
14
}
15
16
String storageDirectory
=
config.getProperty(PROPERTY_FRAMEWORK_STORAGE, DEFAULT_OSGI_STORAGE_DIRECTORY);
17
//
检查storageDirectory合法性
18
if
(storageDirectory.startsWith(WEB_ROOT)) {
19
//
如果以WEB_ROOT常量字符串开头,那么相对于WEB_ROOT来定位
20
storageDirectory
=
storageDirectory.substring(WEB_ROOT.length());
21
storageDirectory
=
context.getRealPath(storageDirectory);
22
}
else
{
23
//
如果是相对路径,那么相对于WEB_ROOT来定位
24
if
(
!
new
File(storageDirectory).isAbsolute()) {
25
storageDirectory
=
context.getRealPath(storageDirectory);
26
}
27
}
28
storageDirectory
=
new
File(storageDirectory).toURL().toExternalForm();
29
config.setProperty(PROPERTY_FRAMEWORK_STORAGE, storageDirectory);
30
if
(logger.isInfoEnabled()) logger.info(
"
Use Framework Storage:
"
+
storageDirectory);
31
32
return
config;
33
}
然后,就可以获取framework实例了,通过framework来初始化,启动和停止OSGi容器:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
try
{
2
framework
=
frameworkFactory.newFramework(configuration);
3
framework.init();
4
5
//
初始化Framework环境
6
initFramework(framework, event);
7
8
//
启动Framework
9
framework.start();
10
11
succeed
=
true
;
12
}
catch
(BundleException e) {
13
throw
new
OSGiStartException(
"
Start OSGi Framework error!
"
, e);
14
}
catch
(IOException e) {
15
throw
new
OSGiStartException(
"
Init OSGi Framework error
"
, e);
16
}
在initFramework方法中,主要做两件事情,一是将当前的ServletContext作为一个service注册到OSGi容器中去:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
private
static
void
registerContext(BundleContext bundleContext, ServletContext servletContext) {
2
Properties properties
=
new
Properties();
3
properties.setProperty(
"
ServerInfo
"
, servletContext.getServerInfo());
4
properties.setProperty(
"
ServletContextName
"
, servletContext.getServletContextName());
5
properties.setProperty(
"
MajorVersion
"
, String.valueOf(servletContext.getMajorVersion()));
6
properties.setProperty(
"
MinorVersion
"
, String.valueOf(servletContext.getMinorVersion()));
7
bundleContext.registerService(ServletContext.
class
.getName(), servletContext, properties);
8
}
第二件事就是:在第一次初始化容器时,加载并启动指定目录中的bundle:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
//
初始化Framework环境
2
private
static
void
initFramework(Framework framework, ServletContextEvent event)
throws
IOException {
3
BundleContext bundleContext
=
framework.getBundleContext();
4
ServletContext servletContext
=
event.getServletContext();
5
6
//
将ServletContext注册为服务
7
registerContext(bundleContext, servletContext);
8
9
File file
=
bundleContext.getDataFile(
"
.init
"
);
10
if
(
!
file.isFile()) {
//
第一次初始化
11
if
(logger.isInfoEnabled()) logger.info(
"
Init Framework
"
);
12
13
String pluginLocation
=
servletContext.getInitParameter(CONTEXT_PARAM_OSGI_PLUGINS_LOCATION);
14
if
(pluginLocation
==
null
) pluginLocation
=
DEFAULT_OSGI_PLUGINS_LOCATION;
15
else
if
(
!
pluginLocation.startsWith(
"
/
"
)) pluginLocation
=
"
/
"
.concat(pluginLocation);
16
17
//
安装bundle
18
File bundleRoot
=
new
File(servletContext.getRealPath(pluginLocation));
19
if
(bundleRoot.isDirectory()) {
20
if
(logger.isInfoEnabled()) logger.info(
"
Load Framework bundles from:
"
+
pluginLocation);
21
22
File bundleFiles[]
=
bundleRoot.listFiles(
new
FilenameFilter() {
23
public
boolean
accept(File dir, String name) {
24
return
name.endsWith(
"
.jar
"
);
25
}
26
});
27
28
if
(bundleFiles
!=
null
&&
bundleFiles.length
>
0
) {
29
for
(File bundleFile : bundleFiles) {
30
try
{
31
bundleContext.installBundle(bundleFile.toURL().toExternalForm());
32
if
(logger.isInfoEnabled()) logger.info(
"
Install bundle success:
"
+
bundleFile.getName());
33
}
catch
(Throwable e) {
34
if
(logger.isWarnEnabled()) logger.warn(
"
Install bundle error:
"
+
bundleFile, e);
35
}
36
}
37
}
38
39
for
(Bundle bundle : bundleContext.getBundles()) {
40
if
(bundle.getState()
==
Bundle.INSTALLED
||
bundle.getState()
==
Bundle.RESOLVED) {
41
if
(bundle.getHeaders().get(Constants.BUNDLE_ACTIVATOR)
!=
null
) {
42
try
{
43
bundle.start(Bundle.START_ACTIVATION_POLICY);
44
if
(logger.isInfoEnabled()) logger.info(
"
Start bundle:
"
+
bundle);
45
}
catch
(Throwable e) {
46
if
(logger.isWarnEnabled()) logger.warn(
"
Start bundle error:
"
+
bundle, e);
47
}
48
}
49
}
50
}
51
}
52
53
new
FileWriter(file).close();
54
if
(logger.isInfoEnabled()) logger.info(
"
Framework inited.
"
);
55
}
56
}
以上就是启动OSGi容器的过程,相比较而言,停止容器就简单多了:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
public
void
contextDestroyed(ServletContextEvent event) {
2
if
(framework
!=
null
) {
3
if
(logger.isInfoEnabled()) logger.info(
"
Stopping OSGi Framework
"
);
4
5
boolean
succeed
=
false
;
6
try
{
7
if
(framework.getState()
==
Framework.ACTIVE) framework.stop();
8
framework.waitForStop(
0
);
9
framework
=
null
;
10
11
succeed
=
true
;
12
}
catch
(BundleException e) {
13
throw
new
OSGiStopException(
"
Stop OSGi Framework error!
"
, e);
14
}
catch
(InterruptedException e) {
15
throw
new
OSGiStopException(
"
Stop OSGi Framework error!
"
, e);
16
}
finally
{
17
if
(logger.isInfoEnabled()) {
18
if
(succeed) logger.info(
"
OSGi Framework Stopped!
"
);
19
else
logger.info(
"
OSGi Framework not stop!
"
);
20
}
21
}
22
}
23
}
最后,还有一件事情,就是将FrameworkConfigListener配置到web.xml中:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
1
<!--
Init OSGi framework
-->
2
<
listener
>
3
<
listener-class
>
org.dbstar.osgi.web.launcher.FrameworkConfigListener
</
listener-class
>
4
</
listener
>
让我们来测试一下吧,在Eclipse中新建一个Server:
另外,在OSGi-Web-Launcher项目的classpath中增加org.eclipse.osgi_3.6.0.v20100128-1430.jar,并且在Java EE Module Dependencies中勾选这个jar,这样可以保证这个jar最终部署到Web Application的WEB-INF/lib目录下去。同样,还需要增加commons-logging.jar。
然后就可以启动这个Server查看效果了。
附上本文中提到的源代码 。