OSGi是一个非常适合实现面向服务的应用(SOA)。
可以让Bundles导出服务,而其他Bundles可以在不了解源Bundles任何信息的情况下消费这些导出的服务。
(1)导出服务:
更新HelloService Bundle,以便能把HelloServiceImpl类的对象导出为服务。(接前面的内容)
A、确认在HelloService Bundle中的MANIFEST.MF文件中导入了org.osgi.framework包
B、在HelloService Bundle中的src文件下创建一个在com.javaworld.sample.helloservice.impl包下的HelloServiceImpl.java文件,代码如下:
- public class HelloServiceActivator implements BundleActivator {
- ServiceRegistrationhelloServiceRegistration;
- public void start(BundleContext context)throws Exception {
- HelloService helloService = newHelloServiceImpl();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
- }
- public void stop(BundleContext context)throws Exception {
- helloServiceRegistration.unregister();
- }
- }
在源Bundle中,使用BundleContext.registerService()方法来导出服务,该方法有三个参数:
i)第一个参数是要注册的服务的接口名称。
如果想把服务注册到多个接口下,需要新建一个String数组存放这些接口名,然后把这个数组作为第一个参数传给registerService()方法。上面代码中,想把服务导出到HelloService接口名下。
ii)第二个参数是要注册的服务的实际JAVA对象。上面代码中,导出HelloServiceImpl类的对象,并将其作为服务。
iii)第三个参数为服务的属性,是一个Dictionary对象。如果多个Bundle导出服务的接口名相同,目标Bundle就可以使用这些属性对源进行过滤,找到感兴趣的服务。
C、最后修改HelloService Bundle中的MANIFEST.MF文件中的Bundle-Activator属性头的值,改为第二步新建文件,即“com.javaworld.sample.helloservice.impl.HelloServiceActivator”。
现在HelloService Bundle就可以导出HelloServiceImpl对象了。
当OSGi容器启动HelloService Bundle时,会将控制权交给HelloServiceActivator.java类,HelloServiceActivator将HelloServiceImpl对象注册为服务。
下面开始创建该服务的消费者。
(2)导入服务:
修改HelloWorld Bundle,以便让它成为HelloService服务的消费者。主要需要修改HelloWorld Bundle中的Activator.java文件。
源码如下:
- importorg.osgi.framework.BundleActivator;
- importorg.osgi.framework.BundleContext;
- importorg.osgi.framework.ServiceReference;
- importcom.javaworld.sample.service.HelloService;
- publicclass Activator implements BundleActivator {
- ServiceReference helloServiceReference;
- public void start(BundleContext context)throws Exception {
- System.out.println("HelloWorld!!");
- helloServiceReference=context.getServiceReference(HelloService.class.getName());
- HelloService helloService=(HelloService)context.getService(helloServiceReference);
- System.out.println(helloService.sayHello());
- }
- public void stop(BundleContext context)throws Exception {
- System.out.println("Goodbye World!!");
- context.ungetService(helloServiceReference);
- }
- }
在上面的代码中,BundleContext.getServiceReference()方法将为注册在HelloService接口下的服务返回一个ServiceReference对象。
如果存在多个HelloService服务,该方法返回排行最高的服务(服务的排行是通过Constrains.SERVICE_RANKING属性指定)。
一旦获得ServiceReference对象,就可以调用BundleContext.getService()方法获得真实的服务对象。
参照运行Bundle的方法运行上面的示例,点击run菜单,并确保同时选中两个Bundle。当启动HelloServiceBundle时,会在控制台看到“InsideHelloServiceImpl.sayHello()”,这个消息是由HelloServiceImpl.sayHello()方法打印出来的。
(3)创建服务工厂:
在上面的示例代码中,使用OSGi框架新建一个Java对象,并把它注册为一个服务,然后让其他的Bundle来消费这个服务。在HelloServiceActivator.start()中,注意到在start()方法中新建了HelloServiceImpl的对象,然后将它注册到HelloService接口名下。代码如下:
- HelloService helloService = newHelloServiceImpl();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
这样注册后,任何其他的Bundle在请求HelloService服务时,OSGi容器将返回同一对象。
[prob]如果要为每个Bundle消费者返回不同的HelloServiceImpl对象,或者服务对象要提供的服务为一个数据库连接,但不是马上打开而是在真正需要的时候才打开这个数据库连接。这时应该如何处理?
[ans]新建一个类实现ServiceFactory接口,并把该类注册为服务,但不是注册实际的服务对象。如果完成了该步骤,其他Bundle在请求该服务时,ServiceFactory实现类将接管该请求,为每个Bundle新建一个服务对象,并把真实服务的创建时间延迟到有人真正需要该服务的时候。
A、新建工厂类HelloServiceFactory.java文件,代码如下:
- public class HelloServiceFactory implements ServiceFactory{
- private int usageCounter = 0;
- public Object getService(Bundle bundle,ServiceRegistration registration) {
- System.out.println("Create objectof HelloService for " + bundle.getSymbolicName());
- usageCounter++;
- System.out.println("Number ofbundles using service " + usageCounter);
- HelloService helloService = newHelloServiceImpl();
- return helloService;
- }
- public void ungetService(Bundle bundle,ServiceRegistration registration, Object service) {
- System.out.println("Release objectof HelloService for " + bundle.getSymbolicName());
- usageCounter--;
- System.out.println("Number ofbundles using service " + usageCounter);
- }
- }
从上面代码可以看到,ServiceFactory接口定义了两个方法:
i)getService()方法:当某一个Bundle第一次使用BundleContext.getService(ServiceReference)方法请求一个服务对象时,OSGi会调用该方法。
使用该方法可以为每一个Bundle返回不同的HelloServiceImpl对象。
若该对象不为null,OSGi会缓存这个对象,如果同一个bundle再次调用该方法,OSGi会返回同一个服务对象。
ii)ungetService()方法:当Bundle释放服务时,OSGi容器调用该方法销毁服务对象。
B、修改HelloService Bundle中的HelloServiceActivator.java的start()方法,让它注册到ServiceFactory接口名下。
- publicclass HelloServiceActivator implements BundleActivator {
- ServiceRegistrationhelloServiceRegistration;
- public void start(BundleContext context)throws Exception {
- HelloServiceFactory helloServiceFactory= new HelloServiceFactory();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);
- }
- public void stop(BundleContext context)throws Exception {
- helloServiceRegistration.unregister();
- }
- }
(4)跟踪服务:
当有多个Bundle使用同一接口名注册服务,这是OSGi容器会返回排行最高的服务,也就是注册时SERVICE_RANKING属性值最大的服务。如果有多个服务排行值相同,选择pid最小的那个服务。
如果服务消费者想了解某一接口的服务对象何时注册、何时取消注册,应该属用ServiceTracker类。
A、修改HelloWorld Bundle中的MANIFEST.MF文件,使其导入org.osgi.util.tracker包
B、新建类HelloServiceTracker.java文件,使其实现ServiceTracker类,代码如下:
- public class HelloServiceTracker extends ServiceTracker {
- public HelloServiceTracker(BundleContext context) {
- super(context, HelloService.class.getName(),null);
- }
- 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.removedService " + reference.getBundle());
- super.removedService(reference, service);
- }
- }
在构造函数中,把HelloService接口名传入其父类中,相当于说,HelloServiceTracker应跟踪注册到HelloService接口名下的所有服务。
HelloServiceTracker继承ServiceTracker,实现了两个方法:
i)addingService()方法:当Bundle使用接口名注册服务时,该方法会被调用。
ii)removingService()方法:当Bundle取消注册某个接口名下的服务时,该方法将被调用。
C、使用HelloServiceTracker类更新Activator.java类,以便让它来管理服务,而不是直接去查找他们。
HelloService Bundle的Activator.java文件源码如下:
- public class Activator implements BundleActivator {
- 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());
- }
- public void stop(BundleContext context) throws Exception {
- System.out.println("Goodbye World!!");
- helloServiceTracker.close();
- }
- }
在start()方法中,首先新建一个HelloServiceTracker对象,然后要求这个对象跟踪HelloService接口下的服务。可以调用getService()方法来获得HelloService对象。