Stefan S.
Stefan S.

Reputation: 4103

How To Create Proxy For OSGi Service

Let's say I have a really simple interface for getting files from somewhere:

interface FileManager {

    File getFile(Object data);
}

We can assume there are multiple implementations of this interface and all applications only use the interface and are blissfully unaware of which implementation the OSGi context provides them with.

Since some methods to get files are really slow, I want to add an optional cache. But I don't want the applications to change from the FileManager interface to another one, since that would make them aware of which implementation they are using (and if it's slow or not).

So I came up with this:

class FileManagerCache implements FileManager {

private final Map<Object, File> cache = new HashMap<>();

public File getFile(final Object data) {
    if (this.cache.containsKey(data)) {
        return this.cache.get(data);
    }
    final File result = getDelegate().getFile(data);
    this.cache.put(data, result);
    return result;
}

private FileManager getDelegate() {
    for (final FileManager fileManager : ServiceUtil.findServices(FileManager.class)) {
        if (this != fileManager) {
            return fileManager;
        }
    }
    throw new UnsupportedOperationException("No FileManager is present!"); //$NON-NLS-1$
}
}

This implementation is registered with a very high "service.ranking" and so the first one the applications use, and it delegates to the next one in line in the list of possible implementations.

Now this approach is not very elegant, and probably error prone. How would I create a proxy in OSGi using standard mechanisms?

Upvotes: 1

Views: 1014

Answers (4)

Felix DependencyManager supports it.

There is the summary from http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/reference/component-aspect.html

Dependency Manager - Aspect

Aspects, as part of aspect oriented programming, can be used in a dynamic environment such as OSGi to "extend" existing services and add certain "capabilities" to them. Examples of these are adding a specific caching mechanism to a storage service or implementing logging. Aspects in OSGi can be applied to services and can be added and removed at runtime.

Aspects allow you to define an "interceptor", or chain of interceptors for a service to add features like caching or logging, etc. An aspect will be applied to any service that matches the specified interface and filter. For each matching service an aspect will be created based on the aspect implementation class. The aspect will be registered with the same interface and properties as the original service, plus any extra properties you supply here. It will also inherit all dependencies, and if you declare the original service as a member it will be injected.

@AspectService(ranking=10), properties={@Property(name="param", value="value")})
class AspectService implements InterceptedService {
     // The service we are intercepting (injected by reflection)
     protected InterceptedService intercepted;

     public void doWork() {
        intercepted.doWork();
     }
 }

Upvotes: 0

Peter Kriens
Peter Kriens

Reputation: 15372

I think what you describe is a 2 service model because you combine multiple responsibilities. You combine the caching responsibility with the abstraction of where the file comes from. Or in other words, your design is mixing concerns and it is therefore not cohesive.

The easiest solution is therefore to have a FileManager and a FileManagerProvider service. You can then provide a cached File Manager and a transparent File Manager depending on your situation.

When I started in software more than 35 years ago I got coupling but it took me many years to understand how much more important cohesion is. This problem is a very archetypical example.

Now to see why this design is bad, you could implement the proxies with OSGi service hooks. You register an alternative and hide the original services. However, that is a lot of work to make a technical inferior solution, as all proxies related solutions have their own problems. Keeping it simple, straightforward, and using actual types to represent your abstractions is imho the best solution. (Though I admit I frequently find that I initially made uncohesive designs as well before I understood the problem well.)

Upvotes: 0

d33t
d33t

Reputation: 1161

Why not just create a service which collects registered implementation of other services? Sample implementation and the idea you can get from here.

Upvotes: -1

Christian Schneider
Christian Schneider

Reputation: 19606

A safer way to define a proxy for another service is to use service properties. For example you could give the slow FileManager a property like "name=A".

Then you could give the proxy the propertie name=A,cached=true. On initialization you could give the proxy a filter name=A to search for a service to proxy.

So the user of the service could either use any serivce (by ranking) or filter for cached=true if it needs the cached variant.

Upvotes: 1

Related Questions