Sunday, September 16, 2012

Declarative Services with Karaf Part 4: Component Factories

Continuing my series on Declarative Services (DS) in Karaf in this article we will take a look at Component Factories and how they behave when using the Service Component Runtime (SCR) in Karaf.

For me, DS Component Factories are most easily thought of as Managed Services in reverse. Why? Because the immediate component is now responsible for managing the life-cycle and configuration of a given service.

Note: The ComponentFactory is very useful for managing a collection of services such as a group of Mina Server threads or say Camel Producer Templates (a bit of foreshadowing).

Lets take a look at a simple example of a Component Factory. In the sources we downloaded in Part 2 of the series change to the component-factory project. Lets first take a look at the GreeterServiceComponentFactory.

Comparing the GreeterServiceFactoryComponentImpl.java with the ManagedGreeterServiceImpl.java code, aside from the class names, the only difference is the attributes that are used to define the component.

GreeterServiceComponentFactoryImpl.java
// The ConfigAdmin PID of our component
@Component(name = GreeterServiceComponentFactoryImpl.COMPONENT_NAME,
// The Factory ID of the Component Factory
factory = "greeter.factory.provider")
public class GreeterServiceComponentFactoryImpl implements
    GreeterServiceComponentFactory {

    {snip....}
}

ManagedGreeterServiceImpl.java
// The ConfigAdmin PID of our component
@Component(name = ManagedGreeterServiceImpl.COMPONENT_NAME,
// The policy that makes our configuration a required dependency
configurationPolicy = ConfigurationPolicy.require)
public class ManagedGreeterServiceImpl implements ManagedGreeterService {

    {snip....}
}

The GreeterServiceComponentFactoryImpl.java uses the factory attribute in place of configurationPolicy. The factory attribute serves two purposes. BND uses it to configure the component as a factory for service creation. It is also used as the targe service property in our component factory manager class. Lets take a look at it now.

GreeterServiceFactoryManager.java
@Component(name = GreeterServiceFactoryManager.COMPONENT_NAME)
public class GreeterServiceFactoryManager {

    public static final String COMPONENT_NAME = "GreeterServiceFactoryManager";

    public static final String COMPONENT_LABEL = "Greeter Service Factory Manager";

    private static final Logger LOG =
        LoggerFactory.getLogger(GreeterServiceFactoryManager.class);

    private ComponentFactory factory;
    private ComponentInstance instance;
    private GreeterServiceComponentFactory greeterService;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * Called when all of the SCR Components required dependencies have been
     * satisfied.
     */
    @Activate
    public void activate() {
        LOG.info("Activating the " + COMPONENT_LABEL);
            final Properties props = new Properties();
            props.setProperty("salutation", "Hello");
            props.setProperty("name", "Scott");
            instance = factory.newInstance(props);
            greeterService = (GreeterServiceComponentFactory)instance.getInstance();
            greeterService.startGreeter();
    }

    /**
     * Called when any of the SCR Components required dependencies become
     * unsatisfied.
     */
    @Deactivate
    public void deactivate() {
        LOG.info("Deactivating the " + COMPONENT_LABEL);
        greeterService.stopGreeter();
        instance.dispose();
    }

    @Reference(target = "(component.factory=greeter.factory.provider)")
    public void setFactory(final ComponentFactory factory) {
        this.factory = factory;
    }

    public void unsetFactory(final ComponentFactory factory) {
        this.factory = null;
    }
}

Note: The actual source contains the necessary thread locks to ensure we are thread safe. Snipped here for clarity.

There are several areas worth calling attention to above. The first is the set/unset bindings. You will notice that we do not bind to an instance of the GreeterServiceComponentFactory but to the DS API provided ComponentFactory service. It is through this service that we will obtain our reference to the GreeterServiceComponentFactory.

Next look at the activate method on line 20. There is where the magic really happens. After our dependency on the ComponentFactory is satisfied the activate method is called. In there we use our ComponentFactory to create an instance of ComponentInstance. The ComponentInstance is responsible for configuration and creation of our GreeterServiceComponentFactory and this happens on line 25. From there we can now create a new instance of the GreeterServiceComponentFactory.

Finally, as we are now responsible for the creation of our components we are also responsible for its clean up. On line 38 after shutting down the Greeter Service we must clean up our reference to the ComponentInstance by calling dispose which removes the configuration associated with this instance of the Greeter Service.

Lets now take a look at how a Component Factory behaves when we deploy it to Karaf using the Karaf SCR Components.

Running the Code
Build and install the code for the compoent-factory. To install execute the following command at the Karaf CLI:
install -s mvn:org.apache.karaf.scr/org.apache.karaf.scr.examples.component.factories/2.2.9
Taking a look at the list of components that are installed you will see that there are actually 3 components and not 2. That is because when we started our manager component obtained an instance of the factory component and used it to create the actual service instance.
scr:list 
   ID   State             Component Name
[21  ] [ACTIVE          ] GreeterServiceFactoryManager
[20  ] [FACTORY         ] GreeterServiceComponentFactory
[22  ] [ACTIVE          ] GreeterServiceComponentFactory
In the log file you will find that the output is slightly different also. In the case of the managed services from Part 3 the service component was always activated first and then its dependent components were activated. Here the manager component has to be activated first since it is responsible for the creation of its dependent service.
INFO  | Karaf Shell Console Thread       | Activating the Greeter Service Factory Manager
INFO  | Karaf Shell Console Thread       | Activating the Greeter Service Component Factory
INFO  | pool-26-thread-1                 | Message 1: Hello Scott
INFO  | pool-26-thread-1                 | Message 2: Hello Scott
INFO  | pool-26-thread-1                 | Message 3: Hello Scott
INFO  | pool-26-thread-1                 | Message 4: Hello Scott
INFO  | pool-26-thread-1                 | Message 5: Hello Scott
If we deactivate the manager we should see that we now have only two components registered with the SCR since the manager is going to dispose of the service instance.
scr:deactivate GreeterServiceFactoryManager
Looking at the list we find just that now.
scr:list 
   ID   State             Component Name
[-1  ] [DISABLED        ] GreeterServiceFactoryManager
[20  ] [FACTORY         ] GreeterServiceComponentFactory
We also find that the manager deactivation preceeds the services deactivation. Again, this is expected since our manager is responsible for the management of the service instance.
INFO  | Karaf Shell Console Thread       | Deactivating the Greeter Service Factory Manager
INFO  | pool-26-thread-1                 | Thread shutting down
INFO  | Karaf Shell Console Thread       | Deactivating the Greeter Service Component Factory

Well that about wraps it up. If you to see the real power of the ComponentFactory in DS try extending the manager to become a managed immediate component that will take the passed in configurations to create many custom greeters. I think you will really start to see the possibilities then.

If you have an questions or comments please feel free to drop me a note.

And watch for my next series: Camel DS with Camel SJMS.

Thanks!

No comments:

Post a Comment