内容源自Hello,osgi
osgi, java语言的动态模块系统,它为模块化应用开发定义了一套基础架构。基于osgi的开源容器框架有:Knoflerfish、Equinox和Apache Felix.通过这些容器,可以把应用程序分成多个模块单元,如此更容易管理各个模块单元之间的交叉依赖关系。
OSGi优点
在不重启容器的情况下,可以对应用程序中的不同模块进行安装、卸载、启动和停止。
容器可以同时运行应用程序中某一模块的多个版本
osgi为开发嵌入式应用、移动应用、富互联网应用提供了优秀的基础架构
在eclipse上创建com.javaworld.sample.helloworld的osgi bundle
New Project --> Plug-in Project
Project Name: com.javaworld.sample.helloworld, an OSGi framework 选择standard,其他项默认
Templates 选择Hello OSGi Bundle,结束。
Bundles:
Workspace勾选默认com.javaworld.sample.helloworld(1.0.0.qualifier)
Target Platform勾选以下
org.apache.felix.gogo.command
org.apache.felix.gogo.runtime
org.apache.felix.gogo.shell
org.eclipse.equinox.console
org.eclipse.osgi #前四个不选会报异常
Arguments: 在后面补充 -clean
-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean
5.启动后,控制台无报错,输入ss成功显示相关bundle.
注:按照以上第四点操作不会出现
Root exception: Workbench has not been created yet.
org.osgi.framework.BundleException: Could not find bundle: org.eclipse.equin
等报错
OSGi 规定的bundle 命令
ss 显示所有已安装的bundles及其状态、id;
start 启动某个bundle
stop 停止某个bundle
update 使用新的jar更新某个bundle
install 安装新的bundle
uninstall 卸载某个已安装的bundle
Bundle 中类的默认访问范围仅对Bundle内部可见,对其他Bundle是不可见。那么如何从一个Bundle访问另外一个Bundle类呢?解决方法是将源Bundle中的包导出来,然后把它们导入到目标Bundle中。
同样创建com.javaworld.sample.service Bundle
1.新建com.javaworld.sample.service.HelloService.java
接口
package com.javaworld.sample.service;
public interface HelloService {
public String sayHello();
}
2.新建com.javaworld.sample.service.impl.HelloServiceImpl.java
类,实现HelloService
接口
package com.javaworld.sample.service.impl;
import com.javaworld.sample.service.HelloService;
public class HelloServiceImpl implements HelloService{
@Override
public String sayHello() {
System.out.println("InsideHelloServiceImple.sayHello()");
return"Say Hello";
}
}
3.导出java包: com.javaworld.sample.service
双击MANIFEST.MF文件进行操作
4.导入java包:project com.javaworld.sample.helloworld导入 包com.javaworld.sample.service
此时,打开com.javaworld.sample.helloworld的Activator.java,此时可以导入com.javaworld.sample.service与com.javaworld.sample.service.impl包。但实际com.javaworld.sample.helloworld能访问的只有com.javaworld.sample.service,无法访问com.javaworld.sample.service.impl.
它可以让Bundles导出服务,而其它的Bundles可以在不必了解源Bundles任何信息的情况下消费这些导出的服务。由于OSGi具有隐藏真实的服务实现类的能力,所以它为面向服务的应用提供了良好的类与接口的组合。
在OSGi框架中,源Bundle在OSGi容器中注册POJO对象,该对象不必实现任何接口,也不用继承任何超类,但它可以注册在一个或多个接口下,并对外提供服务。目标Bundle可以向OSGi容器请求注册在某一接口下的服务,一旦它发现该服务,目标Bundle就会将该服务绑定到这个接口,并能调用该接口中的方法。
上面的二、中提到HelloServiceImpl服务实现类无法访问,可以利用OSGi的导出服务,提供给HelloWorld bundle使用.
导出服务
在HelloService Bundle 中新建HelloServiceActivator.java,注册HelloService接口,对外提供服务。
package com.javaworld.sample.service.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import com.javaworld.sample.service.HelloService;
public class HelloServiceActivator implements BundleActivator{
ServiceRegistration helloServiceRegistration;
@Override
public void start(BundleContext arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("Hello,HelloService!");
HelloService helloService = new HelloServiceImpl();
helloServiceRegistration=arg0.registerService(HelloService.class.getName(), helloService, null);
}
@Override
public void stop(BundleContext arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("Good Bye! Hello Service!");
helloServiceRegistration.unregister();
}
}
注:使用BundleContext.registerService()方法导出服务。
此方法第一个参数: 存放接口名,如果有多个接口,先存放于新建数组中,再将此数组放在此参数;
第二个参数为实现服务类对应的Java对象;第三个参数为服务属性,是一个dictionary对象,当有多个导出服务的接口名相同时,利用此属性进行筛选。
编辑HelloService Bundle的MANIFEST文件将Bundle-Activator的值设为
Bundle-Activator: com.javaworld.sample.service.impl.HelloServiceActivator
导入服务
让HelloWorld Bundle成为HelloService 的服务消费者,修改Activator.java
package com.javaworld.sample.helloworld;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.javaworld.sample.service.*;
import com.javaworld.sample.service.impl.*;
public class Activator implements BundleActivator{
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
ServiceReference helloServiceReference;
public void start(BundleContext context) throws Exception {
System.out.println("Hello World!!");
helloServiceReference = context.getServiceReference(HelloService.class.getName());
HelloService helloService = context.getService(helloServiceReference);
System.out.println(helloService.sayHello());
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye World!!");
context.ungetService(helloServiceReference);
}
}
通过BundleContext.getServiceReference()
方法返回一个ServiceReference
对象(如果存在多个服务,返回排名最高的服务,服务排行由Constants.SERVICE_RANKING
属性指定)。之后通过BundleConetxt.getService(ServiceReference)
方法获取到注册好的真实服务。
此时运行HelloService Bundle,通过控制台输出可以看到已成功访问HelloServiceImpl实现类的方法。
3.创建服务工厂
<1>新建HelloServiceFactory
类,实现org.osgi.framework.ServiceFactory
接口,接口定义了两个方法:
getService()
:某个bundle使用BundleContext.getService(ServiceReference)
请求服务对象时,OSGi框架会调用到该方法。这个方法为每个Bundle新建并返回不同的HelloServiceImpl对象,如果这个对象不是null,OSGi框架会缓存这个对象。如果同一个Bundle再次调用BundleContext.getService(ServiceReference)
方法,OSGi将返回同一个服务对象。
ungetService()`方法:当Bundle释放服务时,OSGi容器可以调用该方法销毁服务对象。
在下面源代码中,使用usageCounter变量来跟踪服务的使用数目,并打印出该服务的客户端数量。
package com.javaworld.sample.helloservice;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import com.javaworld.sample.service.HelloService;
import com.javaworld.sample.service.impl.HelloServiceImpl;
public class HelloServiceFactory implements ServiceFactory{
private int usageCounter=0;
@Override
public Object getService(Bundle bundle, ServiceRegistration registration) {
// TODO Auto-generated method stub
System.out.println("Create objectof HelloService for " + bundle.getSymbolicName());
usageCounter++;
System.out.println("Number of bundles using service " + usageCounter);
HelloService helloService = new HelloServiceImpl();
return helloService;
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration registration,
Object service) {
// TODO Auto-generated method stub
System.out.println("Release objectof HelloService for " + bundle.getSymbolicName());
usageCounter--;
System.out.println("Number of bundles using service " + usageCounter);
}
}
<2>修改HelloServiceActivator
的 start()
方法,让其注册到ServiceFactory
接口下。
package com.javaworld.sample.service.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import com.javaworld.sample.helloservice.HelloServiceFactory;
import com.javaworld.sample.service.HelloService;
public class HelloServiceActivator implements BundleActivator{
ServiceRegistration helloServiceRegistration;
@Override
public void start(BundleContext bc) throws Exception {
// TODO Auto-generated method stub
System.out.println("Hello,HelloService!");
// HelloService helloService = new HelloServiceImpl(); //对比下,这是2.导入服务中的HelloService接口注册
// helloServiceRegistration=bc.registerService(HelloService.class.getName(), helloService, null);
ServiceFactory helloServiceFactory=new HelloServiceFactory();
helloServiceRegistration=bc.registerService(HelloService.class.getName(), helloServiceFactory, null);
}
@Override
public void stop(BundleContext arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("Good Bye! Hello Service!");
helloServiceRegistration.unregister();
}
}
<3>HelloWorld bundle
的Activator.java与2中的不变。
运行HelloServiceBundle
发现,HelloWorld Bundle
也启动并调用注册的服务,计数加1。stop 13
关闭HelloWorld bundle
时,释放HelloServiceBundle
服务,计数变为0.
<1>新建HelloService Tracker Bundle
,对应Projectcom.osgi.util.tracker
,关联com.javaworld.sample.service
<2>新建HelloServiceTracker.java
,如下:
package com.osgi.util.tracker;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import com.javaworld.sample.service.HelloService;
public class HelloServiceTracker extends ServiceTracker{
public HelloServiceTracker(BundleContext context) {
super(context, HelloService.class, null);
// TODO Auto-generated constructor stub
}
public Object addingService(ServiceReference reference){
System.out.println("Inside HelloServiceTracker.addingService"+reference.getBundle());
return super.addingService(reference);
}
public void removedService(ServiceReference reference,Object service){
System.out.println("Inside HelloServiceTracker.removeService"+reference.getBundle());
super.removedService(reference, service);
}
}
构造方法将HelloService接口名传入父类中,此时HelloServiceTracker跟踪注册到HelloService下的所有服务。addingService
方法在Bundle使用接口注册的服务时被调用
removedService
方法在Bundle取消接口注册服务时被调用
<3>修改HelloServiceTracker bundle
的Activator.java
,如下:
package com.osgi.util.tracker;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.javaworld.sample.service.HelloService;
public class Activator implements BundleActivator {
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
HelloServiceTracker helloServiceTracker;
public void start(BundleContext context) throws Exception {
System.out.println("Hello World!!");
helloServiceTracker = new HelloServiceTracker(context);
helloServiceTracker.open();
HelloService helloService= (HelloService) helloServiceTracker.getService();
System.out.println(helloService.sayHello());
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye World!!");
helloServiceTracker.close();
}
}
start()方法中新建了``HelloServiceTracker
对象,要求跟踪HelloService中注册的服务。Trcker调用其getService()
方法可获得HelloService对象。
注:上面的Tracker Bundle也可不创建,直接在HelloWorld Bundle中增加并修改其Activator.java亦可。
<3>运行 HelloService Bundle,start 14,16
将调用到HelloServiceTracker的addingService方法,stop 14,16
将调用到HelloServiceTracker的removedService方法
Hello,HelloService!
Hello World!!
Inside HelloServiceTracker.addingServicecom.javaworld.sample.service_1.0.0.qualifier [12]
Create objectof HelloService for com.osgi.util.tracker
Number of bundles using service 1
InsideHelloServiceImple.sayHello()
Say Hello
Hello World!!
Create objectof HelloService for com.javaworld.sample.helloworld
Number of bundles using service 2
InsideHelloServiceImple.sayHello()
Say Hello
osgi> ss
"Framework is launched."
id State Bundle
0 ACTIVE org.eclipse.osgi_3.10.1.v20140909-1633
2 ACTIVE org.apache.felix.gogo.shell_0.10.0.v201212101605
3 ACTIVE org.eclipse.equinox.console_1.1.0.v20140131-1639
5 ACTIVE org.apache.felix.gogo.runtime_0.10.0.v201209301036
6 ACTIVE org.apache.felix.gogo.command_0.10.0.v201209301215
12 ACTIVE com.javaworld.sample.service_1.0.0.qualifier
14 ACTIVE com.osgi.util.tracker_1.0.0.qualifier
16 ACTIVE com.javaworld.sample.helloworld_1.0.0.qualifier
osgi> stop 16
Goodbye World!!
Release objectof HelloService for com.javaworld.sample.helloworld
Number of bundles using service 1
osgi> stop 14
Goodbye World!!
Inside HelloServiceTracker.removeServicecom.javaworld.sample.service_1.0.0.qualifier [12]
Release objectof HelloService for com.osgi.util.tracker
Number of bundles using service 0
osgi>