I hate to say it because I've done exactly this on several occasions...

But that is a bad design for the reasons you mentioned! However it happens
enough that DS has something that can get your pretty close.

The most recent version of the DS spec which added Field injection and in
particular tuples. If you create a injected field like so:

    @Reference(
        cardinality = ReferenceCardinality.MULTIPLE,
        policy = ReferencePolicy.DYNAMIC,
        policyOption = ReferencePolicyOption.GREEDY
    )
    private volatile List<Map.Entry<Map<String, Object>, ImageFormat>>
_formats;

you get a List of tuples you can iterate over which will hold true to the
natural ordering of the services (via ServiceReference.compareTo)

Then using a Filter (yes this part pulls in OSGi API, but very little) you
can use the streaming API to find "the best" match in the List (if one
exists):

        Filter filter = FrameworkUtil.createFilter("(format=PNG)");

        Optional<ImageFormat> bestMatch = _formats.stream().sorted(
            Collections.reverseOrder() // not totally sure you need this,
but I think so
        ).filter(
            entry -> filter.matches(entry.getKey())
        ).map(
            Map.Entry::getValue
        ).findFirst();

- Ray

On Fri, Jul 21, 2017 at 2:26 PM, Mark Raynsford <list+org.o...@io7m.com>
wrote:

> Hello.
>
> Consider an API like ImageIO. To write an image in the PNG format, one
> calls:
>
>   ImageIO.write(image, "PNG", output);
>
> The implementation of the write() method gets a list of the available
> formats via ServiceLoader and picks the one named "PNG". The "PNG"
> value is the sole criteria used to pick an image format provider.
>
> In OSGi, we'd use the whiteboard pattern (probably with declarative
> services) to implement this. The implementation might look something
> like:
>
>   interface ImageFormat
>   {
>     String name();
>     ...
>   }
>
>   @Component
>   public final class ImageFormats
>   {
>     private final ConcurrentHashMap<String, ImageFormat> formats =
>       new ConcurrentHashMap<String, ImageFormat>();
>
>     @Reference(
>       cardinality = ReferenceCardinality.MULTIPLE,
>       policy = ReferencePolicy.DYNAMIC,
>       unbind = "onFormatUnregister")
>     public void onFormatRegister(
>       final ImageFormat format)
>     {
>       this.formats.put(format.name(), format);
>     }
>
>     public void onFormatUnregister(
>       final ImageFormat format)
>     {
>       this.formats.remove(format.name(), format);
>     }
>
>     ...
>   }
>
> This would work perfectly well, but what happens if two bundles
> try to register a format with the same name? We could check for
> and reject registrations with overlapping names, but that might
> leave developers/users stuck with a format implementation that
> they don't like. What if two different versions of the same bundle
> are installed, and both try to register themselves?
>
> This is a problem that seems to come up in various guises each
> time I work with OSGi, and I've not really seen a satisfactory
> solution to it. Many solutions involve bubbling up the entirely
> internal concern of there being multiple versions of a format
> provider present to the API, and although this can work, it does
> mean that users are then forced to intelligently pick providers
> ("I just want PNG, why do I have to care which of the providers
> I end up with?! Isn't the ImageFormats class supposed to pick
> for me?!"). Part of this problem is caused by the fact that the
> ImageFormats class exists at all; users could, for example,
> search for services providing ImageFormat themselves via DS or
> the OSGi APIs. Sometimes, though, a class analogous to ImageFormats
> is necessary, particularly when you expect callers to be OSGi-unaware
> and/or you only have very simple selection criteria (like a "PNG"
> string).
>
> How do people that know more about OSGi than I do usually handle
> this?
>
> --
> Mark Raynsford | http://www.io7m.com
>
> _______________________________________________
> OSGi Developer Mail List
> osgi-dev@mail.osgi.org
> https://mail.osgi.org/mailman/listinfo/osgi-dev
>



-- 
*Raymond Augé* <http://www.liferay.com/web/raymond.auge/profile>
 (@rotty3000)
Senior Software Architect *Liferay, Inc.* <http://www.liferay.com>
 (@Liferay)
Board Member & EEG Co-Chair, OSGi Alliance <http://osgi.org> (@OSGiAlliance)
_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to