`
classtwo5367
  • 浏览: 38554 次
  • 性别: Icon_minigender_1
  • 来自: Cork
最近访客 更多访客>>
社区版块
存档分类
最新评论

Getting Started with OSGi: Dynamic Service Tracking

阅读更多
Welcome back to the EclipseZone OSGi mini series.

Last time we looked at how to consume a service, using a scenario inspired by Martin Fowler: a MovieLister depends on a MovieFinder to search for movies directed by a specified director. We looked also at strategies for dealing with the dynamic nature of OSGi services, in particular what MovieLister should do if it cannot find an instance of MovieFinder .

There is another possibility that we didn't consider last time: what if there is more than one MovieFinder available? After all, any bundle can register a service under the MovieFinder interface, and all bundles are equal in the eyes of the registry.

We could simply ignore the problem, and in fact that's what our code last time did. By calling the getService() method on a ServiceTracker , we receive an arbitrary single instance of MovieFinder chosen by the service registry. There are various ways to influence the decision (for example a SERVICE_RANKING property that can be specified with the service registration), but as a consumer we will never have full control over this decision. And in fact it's a good thing not to have too much control -- after all, we really should be able to use any instance of MovieFinder . This is somewhat the point of using interfaces in the first place.

Alternatively, it might be useful sometimes to be aware of and use multiple service instances. For example, if there are multiple MovieFinder services available, it probably means that there are multiple sources of movie data that the MovieLister could take advantage of. By using all of them when performing a movie search, we are able to cast the net wider and produce better search results for the user.

Another change we would like to make goes back to the problem discussed from last time: what is the correct thing to do when there is no MovieFinder service available at all? We took the simple route of returning null whenever the listByDirector method was called, and the MovieFinder was unavailable. But what if we made it impossible for methods on MovieLister to be called when the MovieFinder isn't present?

MovieLister is a service, just like MovieFinder . If the MovieFinder service disappears, how about making MovieLister disappear as well? In other words, we want the MovieLister service to have a one-to-many dependency on MovieFinder . In the last part of the tutorial, we had a zero-to-one dependency.

Let's make some changes to the MovieListerImpl class. Replace it with the following:
package osgitut.movies.impl;
 
import java.util.*;
import osgitut.movies.*;
 
public class MovieListerImpl implements MovieLister {
	
	private Collection finders =
		Collections.synchronizedCollection(new ArrayList());
	
	protected void bindFinder(MovieFinder finder) {
		finders.add(finder);
		System.out.println("MovieLister: added a finder");
	}
	
	protected void unbindFinder(MovieFinder finder) {
		finders.remove(finder);
		System.out.println("MovieLister: removed a finder");
	}
	
	public List listByDirector(String director) {
		MovieFinder[] finderArray = (MovieFinder[])
			finders.toArray(new MovieFinder[finders.size()]);
		List result = new LinkedList();
		for(int j=0; j<finderArray.length; j++) {
			Movie[] all = finderArray[j].findAll();	
			for(int i=0; i<all.length; i++) {
				if(director.equals(all[i].getDirector())) {
					result.add(all[i]);
				}
			}
		}
		return result;
	}
	
}



We've actually removed all OSGi dependencies from MovieListerImpl -- it is now a pure POJO. However it requires somebody or something to track the MovieFinder services and supply them to it through the bindFinder method, so to do this we create a new file called osgitut/movies/impl/MovieFinderTracker.java as follows:
package osgitut.movies.impl;
 
import org.osgi.framework.*;
import org.osgi.util.tracker.*;
 
import osgitut.movies.*;
 
public class MovieFinderTracker extends ServiceTracker {
	
	private final MovieListerImpl lister = new MovieListerImpl();
	private int finderCount = 0;
	private ServiceRegistration registration = null;
	
	public MovieFinderTracker(BundleContext context) {
		super(context, MovieFinder.class.getName(), null);
	}
	
	private boolean registering = false; 
	public Object addingService(ServiceReference reference) { 
		MovieFinder finder = (MovieFinder) context.getService(reference); 
		lister.bindFinder(finder); 
 
		synchronized(this) { 
			finderCount ++; 
			if (registering) 
				return finder; 
			registering = (finderCount == 1); 
			if (!registering) 
				return finder; 
		} 
 
		ServiceRegistration reg = context.registerService( 
		MovieLister.class.getName(), lister, null); 
 
		synchronized(this) { 
			registering = false; 
			registration = reg; 
		} 
 
		return finder; 
	} 
 
	public void removedService(ServiceReference reference, Object service) { 
		MovieFinder finder = (MovieFinder) service; 
		lister.unbindFinder(finder); 
		context.ungetService(reference); 
 
		ServiceRegistration needsUnregistration = null; 
		synchronized(this) { 
			finderCount --; 
			if (finderCount == 0) { 
				needsUnregistration = registration; 
				registration = null; 
			} 
		} 
 
		if(needsUnregistration != null) { 
			needsUnregistration.unregister(); 
		} 
	} 
}



This class overrides the ServiceTracker class that we talked about last time, and customizes the way the ServiceTracker behaves when services come and go. Specifically, the addingService method is called when a MovieFinder service is added, and the removedService is called when a MovieFinder is removed. There is also a modifiedService method that we could override, but we don't need it here.

It's worth taking a close look at the code in these two methods. First, in addingService we see that the parameter passed to us is a ServiceReference rather than the actual service implementation object. ServiceReference is a lightweight handle that can be passed freely around as a parameter, and it can be used to obtain the properties of the service, i.e. those that were supplied along with the service registration. Crucially, obtaining a ServiceReference object does not cause the OSGi framework to increment the usage count for the target service. You could therefore think of this as similar to the WeakReference class in the Java Reflection APIs.

The first thing we do with the ServiceReference in our example is to obtain the real MovieFinder service object from it. To do this we need the BundleContext again -- remember, all interaction with the OSGi framework is done through the BundleContext interface. Handily our superclass ServiceTracker keeps the BundleContext reference in a protected member field called context that we can use directly.

Next we bind the finder into our MovieListerImpl using the bindFinder method we defined above, and then we register the MovieListerImpl as a service itself, under the MovieLister interface. Note that we are careful only to register the service if it was not already registered, because in this scenario we want to have a single MovieLister that tracks multiple MovieFinder services.

Finally, we return from the method. There's another interesting point here -- the return type of addingService is simply Object, so what should we return? In fact, the ServiceTracker doesn't care, we can return whatever we like. However, the object we return from addingService will be given back to us when modifiedService and/or removedService are called. So if you look at the first line of our removedService method, you see that we immediately cast the Object to a MovieFinder so that it can be unbound from the lister. Then we unregister the MovieLister service if the number of tracked finders has dropped to zero.

Generally, whatever we do in addingService has to be undone in the removedService method. Therefore we should return from the addingService method whatever will help us find whatever it was we did. This could be a key in a HashMap , or it could be a ServiceRegistration object, or (as in this case) it could be the actual service object.

As a final step in removedService , we have to "unget" the service that we previous "got". This is very important because it causes the service registry to decrement the service usage counter, which allows the service to be freed when the counter drops to zero.

Now we need an activator, osgitut/movies/impl/TrackingMovieListerActivator.java :
package osgitut.movies.impl;
 
import org.osgi.framework.*;
 
public class TrackingMovieListerActivator implements BundleActivator {
 
	private MovieFinderTracker tracker;
 
	public void start(BundleContext context) {
		tracker = new MovieFinderTracker(context);
		tracker.open();
	}
 
	public void stop(BundleContext context) {
		tracker.close();
	}
}



And a manifest, TrackingMovieLister.mf :
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tracking Movie Lister
Bundle-SymbolicName: TrackingMovieLister
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.TrackingMovieListerActivator
Import-Package: org.osgi.framework,
 org.osgi.util.tracker,
 osgitut.movies;version="[1.0.0,2.0.0)"
 



And I will leave it to you as an exercise to build and deploy this bundle to Equinox. Once this is done, try the following sequence of steps to see that everything is working:

   1. Start the BasicMovieFinder and start the TrackingMovieLister. Check that the message "MovieLister: added a finder" appears.
   2. Type the services command and check that there is a MovieLister service registered.
   3. Stop the BasicMovieFinder and check that the message "MovieLister: removed a finder" appears.
   4. Type the services command again and check that there is no MovieLister service registered.


What we have done here has sown the seeds of a very powerful technique. We have tied the lifecycle of one service to the lifecycle of another service -- in fact, multiple other services. Taking this technique further, we could have another service that is tied to the MovieLister , and yet another service that depends on that one, and so on. We end up constructing a graph of interdependent services, but unlike the graphs of beans constructed by some static IOC containers, our graph is robust, self-healing, and able to adjust to a changing world.

On the other hand, there are some problems with the code we've written. The MovieFinderTracker and TrackingMovieListerActivator classes are really just a load of boilerplate, and if we do start expanding this system then we're going to get pretty tired of writing the same code over and over, with just slight modifications each time. Therefore in the next installment we will look at a way for all of that code to be replaced by just a couple of lines of XML!

Also in the next installment we will stop building everything with command line tools in a single directory. My aim when starting this tutorial was to show that OSGi is a simple yet powerful framework, and that you don't need a powerful, heavyweight IDE like Eclipse to develop OSGi bundles. When something appears to be "too easy" there is always the suspicion that the IDE has performed some black magic on our behalf. I hope I have shown that that is not the case, OSGi does not require black magic. On the other hand, if the directory where you have been putting this code looks anything like mine, you're starting to yearn for a proper development environment. So am I. This isn't a problem with OSGi, of course -- any Java project would rapidly get out of control like this if using just the standard tools.

However, I'm afraid that you're going to have to wait until after EclipseCon to read the next part. I'll be jumping on a plane in less than 24 hours on my way to Santa Clara. Hope to see you there!

My thanks go to BJ Hargrave for suggesting a better solution for the concurrent code
分享到:
评论

相关推荐

    Getting Started with OSGi_ Part1

    在标题“Getting Started with OSGi Part1”中,指明了这是一个关于OSGi入门的系列文章中的第一部分。描述部分虽然为“NULL”,但可以从给定的内容中提取出文章的重点信息。标签“源码工具”可能意味着在文章的系列...

    OSGi Technology 教程

    "Getting Started with OSGi 6 Dynamic Service Tracking.doc"继续深化服务跟踪的概念,强调动态性,即在运行时监控和响应服务的变化。 "Getting Started with OSGi 7 Introducing Declarative Services.doc"引入了...

    深入理解OSGi:Equinox原理、应用与最佳实践.pdf

    OSGi(Open Service Gateway Initiative)是一个定义了Java应用程序如何组织和模块化以及如何动态发现、启动、停止、更新这些模块化组件的规范。Equinox是OSGi规范的一个实现,它是由Eclipse基金会开发的。本文将...

    Getting Started with OSGi_ Part3

    在OSGi(Open Service Gateway Initiative)框架中,理解并管理模块间的依赖关系是至关重要的。本篇教程将深入探讨这一主题,帮助开发者们更好地掌握OSGi环境下的程序设计。 一、OSGi概述 OSGi是一种动态模块系统,...

    Getting Started with OSGi_ Part2

    OSGi(Open Services Gateway initiative)是一种Java框架,它定义了服务加载和模块化应用的标准方式。OSGi技术广泛应用于企业级应用开发中,尤其是在Eclipse插件开发和Java EE应用服务器中。OSGi规范定义了如何在...

    深入理解OSGi:Equinox原理、应用与最佳实践源代码+equinox-SDK-3.8源代码

    本资源包括两部分:《深入理解OSGi:Equinox原理、应用与最佳实践》的源代码和equinox-SDK-3.8的源代码。 深入理解OSGi这本书提供了对OSGi,特别是Equinox实现的全面洞察。书中可能涵盖以下几个知识点: 1. **OSGi...

    OSGi_Service

    OSGi(Open Service Gateway Initiative)是一种Java模块化系统,它为创建、部署和管理软件组件提供了一种强大而灵活的方式。在Java世界中,OSGi被视为解决大型复杂应用系统中模块化问题的有效解决方案。下面我们将...

    深入理解OSGi:Equinox原理、应用与最佳实践

    ### 深入理解OSGi:Equinox原理、应用与最佳实践 #### OSGi概述 OSGi(Open Service Gateway Initiative)是一种模块化系统和服务组件模型,它为Java平台提供了一种动态部署、管理和更新应用程序和服务的方法。...

    SpringDM笔记28-Spring And OSGi:Layers of Integration

    标题中的"SpringDM笔记28-Spring And OSGi:Layers of Integration"表明这是一篇关于Spring框架与OSGi(Open Service Gateway Initiative)集成的详细笔记。OSGi是一种模块化系统,它允许Java应用程序以模块化的方式...

    《深入理解OSGi:Equinox原理、应用与最佳实践》

    《深入理解OSGi:Equinox原理、应用与最佳实践》自从1999年OSGi联盟成立以来,OSGi技术随着Java一起飞速发展,它已经成为一种被广泛认可的软件架构技术和方法,许多世界著名的IT企业都加入到OSGi的阵营之中,OSGi...

    深入理解OSGi:Equinox原理、应用与最佳实践.zip

    在《深入理解OSGi:Equinox原理、应用与最佳实践》这本书中,作者深入探讨了OSGi的核心概念、Equinox的工作原理以及如何在实际项目中应用OSGi。这本书的源码可能是为了辅助读者理解和实践书中所讲解的内容。 **OSGi...

    深入理解OSGi:Equinox原理、应用与最佳实践,书本源代码

    在深入理解OSGi:Equinox原理、应用与最佳实践中,我们可以学习到以下几个关键知识点: 1. **模块化编程**:OSGi的核心是模块化,它将应用程序划分为独立的单元,称为服务或bundle。每个bundle都有自己的类路径,...

    《深入理解OSGi:Equinox原理、应用与最佳实践》迷你书

    本书《深入理解OSGi:Equinox原理、应用与最佳实践》深入剖析了OSGi技术的原理和应用,着重介绍了基于OSGi R5.0规范的内容,并结合了Equinox框架的实践经验,旨在帮助读者更好地理解和应用OSGi技术。 本书共分为四...

    OSGi Service Platform Service Compendium

    ### OSGi Service Platform Service Compendium 知识点解析 #### 一、OSGi Service Platform Service Compendium 概览 **OSGi Service Platform Service Compendium** 是由 **OSGi Alliance** 发布的一份关于 OSGi...

    《深入理解OSGi:Equinox原理、应用与最佳实践》附赠光盘

    《深入理解OSGi:Equinox原理、应用与最佳实践》这本书是关于OSGi技术的一部权威著作,其附赠光盘包含丰富的学习资源,旨在帮助读者深入掌握OSGi的精髓,特别是Equinox实现的细节。OSGi(Open Services Gateway ...

    Eclipse RCP与Spring OSGi技术详解与最佳实践

    《Eclipse RCP与Spring OSGi:技术详解与最佳实践》由资源的Eclipse专家亲自执笔,并得到了Eclipse官方技术社区的强烈推荐,权威性毋庸置疑!内容全面,系统讲解了利用Eclipse RCP和Spring OSGi开发大规模Java应用的...

    Pro+Spring+Dynamic+Modules+for+OSGi+Service+Platforms

    I first learned about OSGi when the Eclipse IDE started using it in its plug-in architecture. At the time, the versioning and modularity features seemed nicer “for an IDE” than what was available ...

    OSGi (Open Service Gateway Initiative)入门

    OSGi(Open Service Gateway Initiative)是一个开放标准,它定义了一种模块化系统,用于在Java平台上构建动态、可管理的应用程序。这个技术最初的目标是为家庭网络设备提供服务网关,但随着时间的推移,其应用范围...

Global site tag (gtag.js) - Google Analytics