Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Code Block
    manager.add(createService()
      .setInterface(WebService.class.getName(), null)
      .setImplementation(WebServiceImpl.class)
      .add(createServiceDependency()
        .setService(ConfigurationAdmin.class, null)
        .setRequired(true))
      .add(createServiceDependency()
        .setService(LogService.class, null)
        .setRequired(false))

Specifying configuration dependencies

Configuration dependencies specified here are by definition required. The reason for this is that if you need an optional configuration, simply registering as a ManagedService(Factory) and implementing updated(Dictionary) works fine. For required configuration dependencies, you can use code along these lines:

Code Block

    manager.add(createService()
      .setInterface(HttpServlet.class.getName(), null)
      .setImplementation(MyServlet.class)
      .add(createConfigurationDependency()
        .setPid("org.apache.felix.myservlet")
        .setPropagate(true))

As you can see, you do not need to register as ManagedService yourself. You do need to implement ManagedService in your implementation though. When implementing the updated() method, take the following things into account:

  1. updated() will be called after you are instantiated, but before init() and start() are invoked, or service dependencies are injected, so make sure this method works like this;
  2. if updated() executes normally, this dependency will become resolved;
  3. if updated() throws a ConfigurationException, you are signalling the configuration is invalid and the dependency won't be resolved (or it will become unresolved if it previously was);
  4. if updated() gets a null configuration, the dependency will become unresolved too;

In some cases, you might want to propagate configuration settings to your service properties. In that case, use the setPropagate(true) method to enable automatic property propagation (the default is false). That means any properties you specify yourself and any properties you get from ConfigurationAdmin will be merged, and your service will automatically be updated every time these properties change.

Implementing the service

The service implementation is pretty straightforward. Basically any class that implements the service interface can be used here. Two aspects of the implementation deserve some attention though: the life-cycle model and the service dependencies.

...

Code Block
public class WebServiceImpl implements WebService {
  private volatile ConfigurationAdmin configAdminSvc;
  private volatile LogService logSvc;
  
  public void loadSettings() {
    logSvc.log(LogService.LOG_INFO, "Loading settings.");
    // do stuff here
  }
}

Using an instance factory

(warning) TODO will explain how to provide a factory class and method that will be used to create the service implementation.

Using compositions

Sometimes, a service implementation consists of more than a single instance. Instead, it can be a composition of a number of instances. That might also mean that you need dependencies injected into more than one instance. If that is the case, you can provide a list of instances to use for dependency injection yourself.

To do that, you can specify the name of a method that will return a list of instances, like this:

Code Block

    manager.add(createService()
      .setImplementation(MainServiceImpl.class)
      .setComposition("getInstances"));

Whenever dependencies need to be injected, the dependency manager will query the method and try to inject dependencies into all the instances this method returns (as an Object[]). For example:

Code Block

public class MainServiceImpl {
    private SubComponentA m_compA;
    private SubComponentB m_compB;

    public MainServiceImpl() {
        m_compA = new SubComponentA();
        m_compB = new SubComponentB();
    }

    public Object[] getInstances() {
        return new Object[] { this, m_compA, m_compB };
    }
}

Listening to service dependencies

Optionally, a service can define callbacks for each dependency. These callbacks are invoked whenever a new dependency is discovered or , an existing one is disappearingdependency changes or disappears. They allow you to track these dependencies. This can be very useful if you, for example, want to implement the white board pattern.

Code Block
    manager.add(createService()
      .setImplementation(DeviceListener.class)
      .add(createServiceDependency()
        .setService(Device.class, null)
        .setCallbacks("added", "changed", "removed")
        .setRequired(false));
Code Block
public class DeviceListener {
  public void added(ServiceRegistration regServiceReferende ref, Object service) {
  }
  public void changed(ServiceReference ref, Object service) {
  }
  public void removed(ServiceRegistrationServiceReference regref, Object service) {
  }
}

There are actually several signatures you can use for the callback methods. The dependency manager will search for them in this order (we use a dependency on Device as an example):

  1. ServiceReference, Device
  2. ServiceReference, Object
  3. ServiceReference
  4. Device
  5. Object
  6. (void)

The search ends when the first signature in this list is found. That method will actually be invoked.

Listening to the service state

...