b10y
b10y

Reputation: 879

OSGI Package Visible Service Implementation

I want to accomplish something with OSGI which is very straight-forward in dependency injection frameworks like Guice or Spring.

I want to put the service implementation class into the same package as the service interface. This enables me to use package visible methods which are no concern to the service clients.

The Guice way to accomplish this is simple, make the service implementation class package visible. The service interface:

package com.example.services;

public interface SomeService {

    void scanClasses(ServiceHelper helper);
}

And the implementation:

package com.example.services;

// Package visible service implementation
class SomeServiceImpl implements SomeService {

    @Override
    public void scanClasses(ServiceHelper helper) {
        ClassLoader bundlesClassLoader = helper.getClassLoader();
        // do something with bundle's classes
    }

}

And this is the ServiceHelper class which also has package visible methods:

package com.example.services;

import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWiring;

public class ServiceHelper {
    private ClassLoader bundleClassLoader;

    public ServiceHelper(BundleContext bc) {
        this.bundleClassLoader = bc.getBundle().adapt(BundleWiring.class).getClassLoader();
    }

    // Package visible method to be called from SomeServiceImpl class
    ClassLoader getClassLoader() {
        return bundleClassLoader;
    }

}

But OSGI cannot instantiate ServiceImpl class with package visible decleration or constructor:

java.lang.IllegalAccessException: Class org.eclipse.equinox.internal.ds.model.ServiceComponent can not access a member of class com.example.SomeServiceImpl with modifiers ""

And to complete the example, this is the service client code, which should not be affected by the design:

package com.example.serviceclient;

import org.osgi.framework.BundleContext;

import com.example.services.ServiceHelper;
import com.example.services.SomeService;

public class ServiceClientExample {

    private SomeService someService;

    public void activate(BundleContext bc) {
        someService.scanClasses(new ServiceHelper(bc));
    }

    public void setSomeService(SomeService service) {
        this.someService = service;
    }
}

Putting service implementation into another package, and trying to build good Object oriented encapsulation requires too much work, something like an accessor class which maps package visible methods to the caller, I believe there should be another way.

Upvotes: 0

Views: 270

Answers (1)

Neil Bartlett
Neil Bartlett

Reputation: 23948

You are using OSGi Declarative Services, which does require the implementation class to be public and have a public, zero-arg constructor. This is by design. You could work around this by using a BundleActivator and instantiating the component in code, however there is very little point...

If your implementation class is in the same package as the interface then every client of the interface is directly coupled to the implementation class. So you might as well just make the implementation class public and allow the client to instantiate it directly.

OSGi allows for real separation of contracts, providers and consumers. Coupling the contract and the provider into the same package defeats this. The fact that Guice and Spring make it "straightforward" to couple these concepts together is neither here nor there.

Upvotes: 2

Related Questions