【OSGi】Service
方法呼び出しとの違い:サービスとは、プロバイダとその使用者との間の契約であり、使用者はサービスの具体的な実現に関心を持たず、誰が提供したのかさえ関心を持たず、約束の契約を守ればよい.
サービス向けの設計方式は、ソフトウェア開発をプラグアンドプレイで行うことを奨励します.これは、開発、テスト、導入、メンテナンスの過程でより柔軟性があることを意味します.
従来のJavaは,この問題を依存注入(Dependency Injection,DI)によって解決できる.
欠点:
sensitive dependence on start-up ordering.
また、メタデータ(属性)を指定することはできません.
OSGiはこの問題を動的サービスで解決することができる.複数のサービスインプリメンテーションをサポートし、メタデータに基づいて必要なサービスインプリメンテーションをフィルタリングできます.
公開サービス
public
class WelcomeMailboxActivator
implements BundleActivator {
public
void start(BundleContext context)
throws Exception {
context.registerService(Mailbox.
class.getName(),
new FixedMailbox(), null) ;
//1
}
public
void stop (BundleContext context)
throws Exception {
}
}
BundleContext.registerService()
:interface name、service object、service properties。 String[], 。
bundle , services :
osgi > services
. . .
{ org . osgi . book . reader . api . Mailbox }={ service . id =24}
Registered by bundle : welcome_mailbox_0 . 0 . 0 [ 2 ]
No bundles using service .
. . .
-
start() , stop() ?
, bundle ,OSGi 。 :
serviceRegistration.unregister();
serviceRegistration BundleContext.registerService() 。
-
public
void start ( BundleContext context )
throws Exception {
this.context
= context;
printMessageCount( );
}
private
void printMessageCount()
throws MailboxException {
ServiceReference ref
=
context.getServiceReference (Mailbox.
class.getName());
// 1
if (ref
!= null) {
Mailbox mbox
= (Mailbox)
context.getService(ref);
// 2
if (mbox
!= null) {
try {
int count
= mbox.getAllMessages().length;
// 3
System.out.println(
"There are "
+ count
+
" messages");
}
finally {
context.ungetService(ref);
// 4
}
}
}
1、BundleContext.getServiceReference()
getServiceReference() , 。
2、BundleContext.getService()
null, getService() 。
, null, , null。
3、
mbox.getAllMessages().length。 Java 。
4、BundleContext.ungetService()
ungetService() , 。 bundle , 0, bundle , 。
ungetService() finally ! , 。
—— ! serviceRegistration.unregister()
-
【 】
, , metadata, 。
registerService() java.util.Dictionary ( java.util.Properties), , ; , 。
:
public
void start( BundleContext context)
throws Exception {
Properties props
=
new Properties();
props.put(Mailbox.NAME_PROPERTY,
"welcome");
context.registerService(Mailbox.
class.getName(),
new FixedMailbox(),
props);
}
bundle , services :
osgi > services
. . .
{ org . osgi . book . reader . api . Mailbox }={ mailboxName=welcome , service . id =27}
Registered by bundle : welcome_mailbox_0 . 0 . 0 [ 2 ]
No bundles using service .
. . .
service.id , 。
{org . osgi . book . reader . api . Mailbox } :objectClass。
org.osgi.framework.Constants。
【 】
ServiceReference[] refs
= bundleContext.getServiceReferences(Mailbox.
class.getName(),
"& (mailboxName=welcome) (objectClass=...)");
LDAP 。
ServiceReference.getProperty() ; getPropertyKeys() 。
-
,ServiceReference.getService() null, 。 , 。 ?OSGi :
- service.ranking 。 , 0
- service ID 。 。
-
:
- BundleContext.getServiceReference() ServiceReference
- BundleContext.getService()
? , , 。—— ServiceReference 。
,ServiceReference , bundle 。 , OSGi bundle 。 bundle service , ServiceReference bundle, getService ungetService。
-
start() , , ?
——NO! , 。
?
——NO! .getService(), , 。 。
Whenever a service is either registered or unregistered, the framework publishes a
ServiceEvent to all registered ServiceListeners.
【 】 DataSource , DbMailbox; DataSource DbMailbox。 ?
-
Service
【 】 ServiceListener:
public
void start(BundleContext context)
throws Exception {
this.context
= context;
String filter
=
"("
+Constants.OBJECTCLASS
+
"="
+ DataSource.
class.getName()
+
")";
context.
addServiceListener(
new DbListener (), filter );
}
class DbListener
implements ServiceListener {
DataSource db;
public
void serviceChanged(ServiceEvent event) {
switch () {
case ServiceEvent.REGISTERED
:
this.db
= (DataSource) context.getService(
event.getServiceReference());
... db DbMailbox.....
break;
case ServiceEvent.UNREGISTERED
:
this.db
= null;
break;
....
}
}
}
【 】 Service; Service 。
-
Service + Service
【 】 ; Service, :
public
void start(BundleContext context)
throws Exception {
this.context
= context;
this.listener
=
new DbListener();
synchronized(listener) {
// 1.
String filter
=
"("
+Constants.OBJECTCLASS
+
"="
+ DataSource.
class.getName()
+
")";
context.
addServiceListener(
new DbListener (), filter );
//2. service
ServiceReference[] refs
= context.
getServiceReferences(null, filter);
if (refs
!= null) {
for (ServiceReference ref
: refs) {
this.listener.serviceChanged(
new ServiceEvent(ServiceEvent.REGISTERED, ref));
//
}
}
}
}
Service, Service:
class DbListener
implements ServiceListener {
SortedSet
<ServiceReference
> refs
=
new TreeSet
<ServiceReference
>();
public
void serviceChanged(ServiceEvent event) {
switch () {
case ServiceEvent.REGISTERED
:
refs.add(event.getServiceReference());
... getService() DbMailbox.....
break;
case ServiceEvent.UNREGISTERED
:
refs.remove(event.getServiceReference());
break;
....
}
}
}
public
synchronized DataSource getService() {
if (refs.size()
>
0) {
return (DataSource) context.getService(
refs.last());
}
return null;
}
-
【 1】 service, ?
——NO! 、 , service , service !
, service 2 。 !
【 2】OSGi , , 。
-
ServiceTracker
, 。OSGi ServiceTracker , 。Rather than simply “listening”, which is passive, we wish to actively “track” the ser-vices we depend on.
:
public
class MessageCountActivator2
implements BundleActivator {
private ServiceTracker mboxTracker ;
public
void start(BundleContext context)
throws Exception {
mboxTracker
=
new ServiceTracker(context, Mailbox.class.getName(), null);
//1
mboxTracker
.open();
// 2
printMessageCount ( ) ;
}
public
void stop(BundleContext context)
throws Exception {
mboxTracker
.close();
// 3
}
private
void printMessageCount ( )
throws MailboxException {
Mailbox mbox
= (Mailbox)
mboxTracker.getService();
// 4 service?——
if (mbox
!= null) {
int count
= mbox.getAllMessages().length ;
// 5
System.out.println(
"There are "
+ count
+
" messages");
}
}
}
ServiceTracker.getService(), ServiceTracker.waitForService (5000) ; , 5 。
:
The first difference, which we can see at marker 1 of the start method, is that instead of saving the bundle context directly into a field, we instead construct a new ServiceTracker field, passing it the bundle context and the name of the service that we are using it to track. Next at marker 2, we “open” the tracker, and at marker 3 in the stop() method we “close” it.
—— ServiceTracker
The next difference is at marker 4, where we call getService on the tracker. Refreshingly, this immediately gives us the actual service object (if available) rather than a ServiceReference. So we simply go ahead and call the service at marker 5, bearing in mind that we still need to check if the service was found. Also, we don’t need to clean up after ourselves with a finally block: we simply let the variable go out of scope, as the tracker will take care of releasing the service.
——getService , ServiceReference; , ungetService, ServiceTracker 。
-
ServiceTrackerCustomizer
DbMailbox , :
public
class DbMailboxActivator
implements BundleActivator {
private BundleContext context;
private ServiceTracker tracker;
public
void start(BundleContext context)
throws Exception {
this.context
= context ;
tracker
=
new ServiceTracker(context, DataSource.
class.getName(),
new DSCustomizer());
// 1
// 2
// When we call open() on a service tracker, it hooks up a ServiceListener
// and then scans the pre-existing services, eliminates duplicates etc.
tracker.open();
}
public
void stop(BundleContext context)
throws Exception {
tracker.close();
// 3
}
private
class DSCustomizer
implements ServiceTrackerCustomizer {
// when we return null from addingService, which the tracker takes to mean that we don’t care about this particular service reference.
// That is, if we return null from addingService, the tracker will “forget” that particular service reference and will not call either modifiedService or removedService later if it is modified or removed.
// Thus returning null can be used as a kind of filter, but in the next section we will see a more convenient way to apply filters declaratively
public
Object
addingService(ServiceReference ref) {
DataSource ds
= (DataSource)context.getService(ref);
// 4
DbMailbox mbox
=
new DbMailbox(ds);
ServiceRegistration registration
= context.registerService(Mailbox.
class.getName(), mbox, null);
// 5
return registration;
// 6
}
public
void
modifiedService(ServiceReference ref,
Object service ) {
}
public
void
removedService(ServiceReference ref,
Object service ) {
ServiceRegistration registration
= (ServiceRegistration) service;
// 7 service, addingService()
registration.unregister();
// 8
context.ungetService(ref);
// 9
}
}
}
1、 ServiceTracker:we pass in an instance of the ServiceTrackerCustomizer interface, which tells the tracker what to do when services are added, removed or modified.
2、ServiceTracker.open(): ServiceListener, , 。
3、ServiceTracker.close():
4&5、addingService(): DataSource service 、 service ,
6、addingService() : modifiedService() removedService()。
null, ,tracker , , modifiedService() removedService()。
null “ ”, Filter 。
7&8&9、 DataSource service reference , , DataSource service。
, service addingService(), service addingService()。 service、 service。
unlike a listener, the adding and removed methods of ServiceTracker are called not only when the state of a service changes but also when the tracker is opened, to notify us of pre-existing services.
The addingService() method is called multiple times when the tracker is opened, once for each service currently registered, and it is also called whenever a new service is registered at any time later for as long as the tracker is open.
Furthermore the removedService() is called any time a service that we have been previously been notified of goes away, and it is also called for each service when the tracker closes.
Therefore we can deal with services in a uniform fashion without needing to distinguish between pre-existing services and ones that are registered while our listener is active. This greatly simplifies the code we need to write.
-
Filter
Filter filter
=
FrameworkUtil.createFilter (
" (&( objectClass ="
+ Mailbox.
class.getName()
+
")"
+
"( mailboxName = welcome )( lang = en ))" ) ;
tracker
=
new ServiceTracker(context, filter, null ;
getServiceReference Properties :
context.getServiceReference(Mailbox.
class.getName(),
" (&( mailboxName = welcome )( lang = en )) " )
FrameworkUtil.createFilter() InvalidSyntaxException, 。
, String.format(); , :
context.createFilter (
String.format(
" (&(%s =%s )(%s =%s )(%s=%s))" ,
Constants.OBJECTCLASS, Mailbox.
class.getName(),
Mailbox.NAME_PROPERTY,
"welcome",
"lang",
"en*")) ;
【 】
- service bundle 。 logger , bundle 。
- Service:ServiceFactory allows us to delay the creation of service objects until they are actually needed.
【 】
, ServiceFactory。The factory does not itself implement the service interface, but it knows how to create an object that does.
, Service ServiceFactory。
【 】
ServiceFactory :
public
interface ServiceFactory {
public Object getService ( Bundle bundle, ServiceRegistration registration ) ;
public
void ungetService ( Bundle bundle, ServiceRegistration registration, Object service ) ;
}
getService 。 1-bundle bundle, 2-registration ServiceRegistration。
—— ServiceFactory ; ServiceRegistration 。
ungetService bundle , 。 3-service getService 。
【 】
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
class LogImpl
implements Log {
private String sourceBundleName ;
public LogImpl(Bundle bundle) {
this.sourceBundleName
= bundle.getSymbolicName();
}
public
void log(String message) {
System.out.println(sourceBundleName
+
": "
+ message );
}
}
public
class
LogServiceFactory
implements ServiceFactory {
public Object getService(Bundle bundle, ServiceRegistration registration ) {
return
new LogImpl(bundle) ;
}
public
void ungetService(Bundle bundl, ServiceRegistration registration, Object service) {
// No spec i a l clean −up required
}
}
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public
class LogServiceFactoryActivator
implements BundleActivator {
public
void start(BundleContext context)
throws Exception {
context.registerService( Log.
class.getName(),
new LogServiceFactory(), null) ;
}
public
void stop(BundleContext context)
throws Exception {
}
}