• No results found

4. Services

4.9. Filtering on Properties

4.9. Filtering on Properties

Section 4.5 described how properties can be added to services when they are registered, and how the properties on a ServiceReference can be in- trospected. Now let’s look at another important use for properties: filtering service look-ups.

Both of the look-up code samples we saw, in Sections 4.4 and 4.6, obtained a single instance of the mailbox service. Yet it should be clear by now that there can be an arbitrary number of service instances for each particular type, because any bundle can register a new service under that type. Therefore it is sometimes necessary to further restrict the set of services obtained by a look-up. This is done with filters, which are applied to the properties of the service. A filter is a simple string, using a format which is easy to construct either manually or programmatically.

For example, suppose we wish to find the “welcome” mailbox service, and not any other kind of mailbox. Recall that that mailbox service has a property named mailboxName with the value “welcome”. The filter string required to find this service is simply:

(m a i l b o x N a m e=w e l c o m e)

Suppose we added a further property to the welcome mailbox indicating the language. To find the English version, which should have the lang property set to “en”, we construct the following composite filter:

(&(m a i l b o x N a m e=w e l c o m e) (l a n g=e n) )

Some languages have variants, such asen_UKanden_USfor British and Amer- ican English respectively. Suppose we want to match any kind of English: (&(m a i l b o x N a m e=w e l c o m e) (l a n g=e n∗ ) )

Finally, suppose we want either German (“de”) or any form of Englishexcept

Canadian:

(&(m a i l b o x N a m e=w e l c o m e) ( | (l a n g=d e) (l a n g=e n∗ ) ) ( ! (l a n g=e n _ C A) ) )

This syntax is borrowed directly from LDAP search filters, as defined in [?]. A filter is either a simple operation, or a composite. Here are some examples of simple operations:

(foo=*) Propertyfoois present

(foo=bar) Value of propertyfoois equal to “bar” (count>=1) Value of propertycountis 1 or greater (count<=10) Value of propertycountis 10 or less

Composite filters can be built up from simple filters, or they might compose filters which are themselves composites. Here are the composition operations:

(!(filter)) Boolean “NOT”: filter is false (&(filter1)...(filterN)) Boolean “AND”: all filters are true (|(filter1)...(filterN)) Boolean “OR”: any one filter is true

So how do we use these filters? It depends how we are doing the service look- up. If we are using the low-level approach from Section4.4 then we simply use an alternative signature for thegetServiceReferencemethod that takes a filter in addition to a service interface name:

c o n t e x t.g e t S e r v i c e R e f e r e n c e(M a i l b o x.c l a s s.g e t N a m e( ) , " ( & ( m a i l b o x N a m e = w e l c o m e ) ( l a n g = e n ) ) ") ;

If we are using a service tracker as in Sections4.6 or 4.8, we need to use an alternative constructor for the ServiceTracker class which takes a Filter objectinstead of a service interface name. Filterobjects can be constructed by a call to a static utility method,FrameworkUtil.createFilter:

F i l t e r f i l t e r = F r a m e w o r k U t i l.c r e a t e F i l t e r(

" ( & ( o b j e c t C l a s s = " + M a i l b o x.c l a s s.g e t N a m e( ) + " ) " + " ( m a i l b o x N a m e = w e l c o m e ) ( l a n g = e n ) ) ") ;

t r a c k e r = n e w S e r v i c e T r a c k e r(c o n t e x t, f i l t e r, n u l l) ;

The filter object replaces the service interface name parameter because we can track instances of multiple service types with the same tracker. However when we do wish to restrict the tracker to a single service type, we need to include that constraint in the filter using the built-in property nameobjectClass. In fact, the constructor we were using previously is simply a convenience wrapper for the filter-based constructor.

While we can construct filters easily using string concatenation as above, it can be somewhat error prone — it is all too easy to miss closing a parenthesis. Therefore it’s a good idea to use theString.formatmethod which was intro- duced in Java 5. This method uses a string pattern in the style of the standard printffunction in the C programming language, so we can construct a filter as shown in Listing 4.13. Each %s in the format string is a format specifier

which is replaced by one argument from the argument list. The resulting code is slightly longer, but it is much harder to make a mistake in the syntax, and it is easier to refer to constants defined elsewhere.

Listing 4.13Building Filter Strings usingString.format

c o n t e x t.c r e a t e F i l t e r(S t r i n g.f o r m a t(" ( & ( % s = % s ) ( % s = % s ) ( % s = % s ) ) ", C o n s t a n t s.O B J E C T C L A S S, M a i l b o x.c l a s s.g e t N a m e( ) ,

M a i l b o x.N A M E _ P R O P E R T Y, " w e l c o m e ", " l a n g ", " e n * ") ) ;

Unfortunately the createFilter method of FrameworkUtil can throw an InvalidSyntaxException, which is a checked exception, so we must always