Dependency Manager - Resource Adapter

Resource adapters work just like adapters, but instead of working with services, they work with resources. Resources, represented as a URL, are an abstraction introduced to provide a generic way of dealing with "blobs" and can be resources inside a bundle, filesystem or some kind of data store.

A resource adapter will be applied to any resource that matches the specified filter condition. For each matching resource an adapter will be created based on the adapter implementation class. The adapter will be registered with the specified interface and existing properties from the original resource 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.

To define a resource adapter, you first need to create a ResourceComponent component using the DependencyActivatorBase.createResourceComponent() or the DependencyManager.createResourceComponent() method.

The ResourceComponent extends the Component and provides some additional setter methods related to resource adapters.

Usage Example:

Here, the "VideoPlayer" service provides a video service on top of any movie resources, with service properties "host"/"port"/"protocol"/"path" extracted from the resource URL. There will be one VideoPlayerImpl instantiated and started for each available URL:

public class VideoPlayerImpl implements VideoPlayer {
    // Injected by reflection
    volatile URL resource;

    void play() {} // play video referenced by this.resource
    void stop() {} // stop playing the video
    void transcode() {} // ...
}

public class Activator extends DependencyManagerActivator {
    public void init(BundleContext ctx, DependencyManager dm) throws Exception {
        Component c = manager.createResourceComponent()
            .setResourceFilter("(&(path=/videos/*.mkv)(host=localhost))")
            .setPropate(true)
            .setInterface(VideoPlayer.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
            .setImplementation(VideoPlayerImpl.class);
        dm.add(c);
    }
}

Notice that resource adapters are only supported with the DM API, not using annotations or using DM lambda.

Now, here is the resource provider, which provides two URLS:

public class Activator extends DependencyActivatorBase {
    @Override
    public void init(BundleContext ctx, DependencyManager dm) throws Exception {
    	// add resource provider
        URL[] resourceURLs = new URL[] {
        		new URL("file://localhost/path/to/file1.txt"),
        		new URL("file://localhost/path/to/file2.txt")
        };

        Component resourceProvider = createComponent()
        		.setImplementation(new ResourceProvider(ctx, resourceURLs))
        		.add(dm.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove"));
        dm.add(resourceProvider);
    }
}

class ResourceProvider {
	final URL[] m_resources;
    final BundleContext m_context;
    final Map<ResourceHandler, Filter> m_handlers = new HashMap<>();

	ResourceProvider(BundleContext ctx, URL ... resources) {
		m_context = ctx;
		m_resources = resources;
	}

	public void add(ServiceReference<?> ref, ResourceHandler handler) {
        String filterString = (String) ref.getProperty("filter");
        Filter filter = null;
        if (filterString != null) {
            try {
                filter = m_context.createFilter(filterString);
            }
            catch (InvalidSyntaxException e) {
                return;
            }
        }
        for (int i = 0; i < m_resources.length; i++) {
            if (filter == null || filter.match((Dictionary<String, ?>) ResourceUtil.createProperties(m_resources[i]))) {
                synchronized (m_handlers) {
                    m_handlers.put(handler, filter);
                }
                handler.added(m_resources[i]);
            }
        }
    }

    public void remove(ServiceReference<?> ref, ResourceHandler handler) {
        Filter filter;
        synchronized (m_handlers) {
            filter = (Filter) m_handlers.remove(handler);
        }
        if (filter != null) {
            for (int i = 0; i < m_resources.length; i++) {
                if (filter == null || filter.match((Dictionary<String, ?>) ResourceUtil.createProperties(m_resources[i]))) {
                    handler.removed(m_resources[i]);
                }
            }
        }
    }
}