I am so very excited. I was inducted into the Apache brotherhood for the first time joining Camel as an official committer. I am just seriously geeking out about this.
To kewl.
Scott England-Sullivan's Blog
Nuggets from a born again Java Enterprise Developer...
Wednesday, September 19, 2012
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
ManagedGreeterServiceImpl.java
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
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:
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!
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.9Taking 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 ] GreeterServiceComponentFactoryIn 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 ScottIf 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 GreeterServiceFactoryManagerLooking at the list we find just that now.
scr:list ID State Component Name [-1 ] [DISABLED ] GreeterServiceFactoryManager [20 ] [FACTORY ] GreeterServiceComponentFactoryWe 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!
Declarative Services with Karaf Part 3: Managed Services
Continuing my series on Declarative Services in Karaf in this article we will take a look at Managed Services and how they behave when using the Service Component Runtime (SCR) in Karaf. Managed Services are OSGi services that need access to configuration data at initialization and/or run-time. These configurations are administered through the OSGi Configuration Admin service which is a core service in Karaf. Karaf provides three ways to manage configurations used by the Config Admin service: the Felix File Install framework, the Config Admin CLI Commands and the Web Console.
The Felix File Install framework is configured to monitor the $KARAF_HOME/etc directory for bundle configuration files. When files are added with a name that matches the Managed Service PID and ending with .cfg, the file is read and a new configuration is added to the Config Admin registry.
Lets take a look at the second example that was included in the code we downloaded from Part 2: managed-service.
The managed-service project it is basically the same as what we found in the service project from Part 2. We have a delayed component, the ManagedGreeterService, and an immediate component, the ManagedGreeterComponent.
The ManagedGreeterComponent is mostly the same as what was in the GreeterComponent from Part 2. The only real difference is that it calls the new interface from the activate and deactivate methods. The annotations have not changed though so lets take a look at our delayed component.
Since we are discussing a managed service it makes sense to create a service that can demonstrate how creating, changing and removing the configuration for a given managed service behaves when using DS. To explore this we have created a new interface that allows a consumer of the delayed component to stop and start its logic:
ManagedGreeterService.java
The ManagedGreeterServiceImpl.java code has a number of changes that are worth pointing out. First, in lines 47-77 we have added a runnable that when started, will print to our log until the stop method is called. This runnable requires configuration before it is started though. That is going to be handled by the managed part of our delayed component.
First look at the activate method on line 17. The signature has changed by adding a Map argument (one of the three possible arguments you can pass to the any of the lifecycle methods in DS, see the spec for further details). The SCR will recognize this signature and will ask the ConfigAdmin service for a configuration that corresponds to the components name, in this case, the ManagedGreeterService.
Remember that a delayed component will activate when all of its dependencies are satisfied and that there is an active reference to it. In this case the dependency is not another service but the configuration. The SCR annotations though configure our components configuration to be optional by default though. Therefore we need to tell the SCR that a configuration is required and to do that we set the annotation attribute configurationPolicy to ConfigurationPolicy.require on line 2. This will make a configuration required for our component before it will become satisfied.
ManagedGreeterServiceImpl.java
Running the Code
Lets see how our new code behaves in Karaf using the SCR commands. First lets install it:
To verify it was installed run scr:list from the Karaf CLI.
Both components should be UNSATISFIED because we have not created a configuration yet for the ManagedGreeterService. Lets create one then and see what happens. From the Karaf CLI issue the following configuration commands:
Lets remove the configuration now and verify that everything shuts down and is cleaned up:
Updating at Run-Time
Now what happens when we change the configuration without shutting the components down? To find out lets update it. Issue the following command on the Karaf CLI:
This cascading behavior of available dependencies allows our components to start and stop in a very predictable manor.
The Modified Life-cycle
In DS we have another lifecycle method that allows us to update the configuration of a component dynamically. It is referred to the "modified" life-cycle and we use the @Modified annotation to denote the method we choose to handle the modification. In our case we use the modified(Map properties) signature so the SCR knows to pass in the updated properties from the ConfigAdmin service. Here is the new code:
So what happened? Before when we only had the activate & deactivate method defined the activate & deactivate life-cycles were used to manage all updates of the component. This allowed our components to de/activate cleanly. When the component is configured with the modified life-cycle the SCR will only call the modified method which never causes the ManagedGreeterService to become UNSATISFIED. Therefore any component with a dependency on the ManagedGreeterService is never notified that there has been a change in the state of the component and they continue upon their merry way oblivious to the changes.
WRT thread safety, the same actually goes for our set/unset methods as well in the ManagedGreeterComponent. These methods along with the life-cycle methods are called by separate threads in the SCR. If you remember back in Part 1 in my Editorial Opinion, I touched on the importance of creating bind/unbind methods to allow our components to lock be thread safe. If you look at the ManagedGreeterComponent source code you will see where we make sure to wrap our service in lock blocks to ensure that it maintain thread safety.
That should give you a pretty good overview of Managed Services using DS in Karaf. In Part 4 of this series, we will take a look at using components to configure components use DS Factory components.
Next Article:
Part 4: Component Factories
Previous Articles:
Part 1: Getting Started
Part 2: The Basics
References:
Karaf
Felix File Install
OSGi 4.2 Specification
BND Annotations
The Felix File Install framework is configured to monitor the $KARAF_HOME/etc directory for bundle configuration files. When files are added with a name that matches the Managed Service PID and ending with .cfg, the file is read and a new configuration is added to the Config Admin registry.
Lets take a look at the second example that was included in the code we downloaded from Part 2: managed-service.
The managed-service project it is basically the same as what we found in the service project from Part 2. We have a delayed component, the ManagedGreeterService, and an immediate component, the ManagedGreeterComponent.
The ManagedGreeterComponent is mostly the same as what was in the GreeterComponent from Part 2. The only real difference is that it calls the new interface from the activate and deactivate methods. The annotations have not changed though so lets take a look at our delayed component.
Since we are discussing a managed service it makes sense to create a service that can demonstrate how creating, changing and removing the configuration for a given managed service behaves when using DS. To explore this we have created a new interface that allows a consumer of the delayed component to stop and start its logic:
ManagedGreeterService.java
public interface ManagedGreeterService { void startGreeter(); void stopGreeter(); }
The ManagedGreeterServiceImpl.java code has a number of changes that are worth pointing out. First, in lines 47-77 we have added a runnable that when started, will print to our log until the stop method is called. This runnable requires configuration before it is started though. That is going to be handled by the managed part of our delayed component.
First look at the activate method on line 17. The signature has changed by adding a Map argument (one of the three possible arguments you can pass to the any of the lifecycle methods in DS, see the spec for further details). The SCR will recognize this signature and will ask the ConfigAdmin service for a configuration that corresponds to the components name, in this case, the ManagedGreeterService.
Remember that a delayed component will activate when all of its dependencies are satisfied and that there is an active reference to it. In this case the dependency is not another service but the configuration. The SCR annotations though configure our components configuration to be optional by default though. Therefore we need to tell the SCR that a configuration is required and to do that we set the annotation attribute configurationPolicy to ConfigurationPolicy.require on line 2. This will make a configuration required for our component before it will become satisfied.
ManagedGreeterServiceImpl.java
@Component(name = ManagedGreeterServiceImpl.COMPONENT_NAME, configurationPolicy = ConfigurationPolicy.require) public class ManagedGreeterServiceImpl implements ManagedGreeterService { public static final String COMPONENT_NAME = "ManagedGreeterService"; public static final String COMPONENT_LABEL = "Managed Greeeter Service"; private static final Logger LOG = LoggerFactory.getLogger(ManagedGreeterServiceImpl.class); private ExecutorService executor = Executors.newCachedThreadPool(); private Worker worker = new Worker(); /** * Called when all of the SCR Components required dependencies have been * satisfied. */ @Activate public void activate(final Map<string> properties) { LOG.info("Activating the " + COMPONENT_LABEL); if (properties.containsKey("salutation")) worker.setSalutation((String)properties.get("salutation")); if (properties.containsKey("name")) worker.setName((String)properties.get("name")); } /** * Called when any of the SCR Components required dependencies become * unsatisfied. */ @Deactivate public void deactivate() { LOG.info("Deactivating the " + COMPONENT_LABEL); } public void startGreeter() { executor.execute(worker); } public void stopGreeter() { if (!executor.isTerminated()) { executor.shutdownNow(); } } /** * Thread worker that continuously prints a message. */ private class Worker implements Runnable { private String name; private String salutation; public void run() { boolean running = true; int messageCount = 0; while (running) { try { LOG.info("Message " + (++messageCount) + ": " + salutation + " " + name); Thread.sleep(1000); } catch (InterruptedException e) { running = false; LOG.info("Thread shutting down"); } } } public void setName(String userName) { this.name = userName; } public void setSalutation(String salutation) { this.salutation = salutation; } } }
Running the Code
Lets see how our new code behaves in Karaf using the SCR commands. First lets install it:
install -s mvn:org.apache.karaf.scr/org.apache.karaf.scr.examples.managed.service/2.2.9
To verify it was installed run scr:list from the Karaf CLI.
scr:list ID State Component Name [7 ] [UNSATISFIED ] ManagedGreeterComponent [8 ] [UNSATISFIED ] ManagedGreeterService
Both components should be UNSATISFIED because we have not created a configuration yet for the ManagedGreeterService. Lets create one then and see what happens. From the Karaf CLI issue the following configuration commands:
config:edit ManagedGreeterService config:propset salutation "Hello" config:propset name "Scott" config:updateNow when we use the scr:list command we see that our components are now SATISFIED
scr:list ID State Component Name [9 ] [ACTIVE ] ManagedGreeterComponent [8 ] [ACTIVE ] ManagedGreeterServiceAnd if we look into our log file we will start to see the output from our ManagedGreeterService:
INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeeter Service INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeter Component INFO | pool-19-thread-1 | Message 1: Hello Scott INFO | pool-19-thread-1 | Message 2: Hello Scott INFO | opt/karaf/apache-karaf-2.2.9/etc | Installed /opt/karaf/apache-karaf-2.2.9/etc/ManagedGreeterService.cfg INFO | pool-19-thread-1 | Message 3: Hello Scott INFO | pool-19-thread-1 | Message 4: Hello Scott INFO | pool-19-thread-1 | Message 5: Hello Scott ...Note: Observe on line 5 that when we use the CLI to add a configuration a ManagedGreeterService.cfg file is added to the etc directory.
Lets remove the configuration now and verify that everything shuts down and is cleaned up:
config:delete ManagedGreeterServiceUsing the scr:list command again we see that our components have become UNSATISFIED and have deactivated. Reviewing the log file verifies this.
INFO | vent: pid=ManagedGreeterService) | Deactivating the Managed Greeter Component INFO | pool-19-thread-1 | Thread shutting down INFO | vent: pid=ManagedGreeterService) | Deactivating the Managed Greeeter Service INFO | opt/karaf/apache-karaf-2.2.9/etc | Uninstalled /opt/karaf/apache-karaf-2.2.9/etc/ManagedGreeterService.cfgBoth services are shut down and the CFG is removed.
Updating at Run-Time
Now what happens when we change the configuration without shutting the components down? To find out lets update it. Issue the following command on the Karaf CLI:
config:edit ManagedGreeterService config:propset salutation "Bonjour" config:propset name "Sully" config:updateNow lets go back and update the configuration with new values:
config:edit ManagedGreeterService config:propset salutation "Bonjour" config:propset name "Sully" config:updateIf we take a look at the log file we see that the components behaved in a very predictable manor. They were activated upon the initial configuration and then when the configuration changed the SCR was notified of the change and then proceeded to deactivate the ManagedGreeterComponent so it could deactivate and then reactivate the ManagedGreeterService with the new configuration. When completed the SCR called the activate on the ManagedGreeterComponent and we were off and running again.
INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeeter Service INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeter Component INFO | pool-20-thread-1 | Message 1: Hello Scott INFO | pool-20-thread-1 | Message 2: Hello Scott INFO | opt/karaf/apache-karaf-2.2.9/etc | Installed /opt/karaf/apache-karaf-2.2.9/etc/ManagedGreeterService.cfg INFO | pool-20-thread-1 | Message 3: Hello Scott INFO | pool-20-thread-1 | Message 4: Hello Scott INFO | pool-20-thread-1 | Message 5: Hello Scott INFO | vent: pid=ManagedGreeterService) | Deactivating the Managed Greeter Component INFO | pool-20-thread-1 | Thread shutting down INFO | vent: pid=ManagedGreeterService) | Deactivating the Managed Greeeter Service INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeeter Service INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeter Component INFO | pool-21-thread-1 | Message 1: Bonjour Sully INFO | pool-21-thread-1 | Message 2: Bonjour Sully INFO | pool-21-thread-1 | Message 3: Bonjour Sully INFO | pool-21-thread-1 | Message 4: Bonjour Sully INFO | pool-21-thread-1 | Message 5: Bonjour Sully
This cascading behavior of available dependencies allows our components to start and stop in a very predictable manor.
The Modified Life-cycle
In DS we have another lifecycle method that allows us to update the configuration of a component dynamically. It is referred to the "modified" life-cycle and we use the @Modified annotation to denote the method we choose to handle the modification. In our case we use the modified(Map properties) signature so the SCR knows to pass in the updated properties from the ConfigAdmin service. Here is the new code:
@Modified public void modified(final Map<string> properties) { LOG.info("Modifying the " + COMPONENT_LABEL); // This time we really only need to make sure if it changed it isn't // empty if (properties.containsKey("salutation") && !properties.get("salutation").equals("")) { worker.setSalutation((String)properties.get("salutation")); } // Same for name if (properties.containsKey("name") && !properties.get("name").equals("")) { worker.setName((String)properties.get("name")); } }Before testing it out lets clean up our old configuration by issuing the config:delete ManagedGreeterService command at the Karaf CLI. Now if you are using the supplied example code you can uncomment the modified method, rebuild it and issue the update command on the Karaf CLI for the managed services bundle. Once that is done lets go back and issue the two different configuration update commands:
config:edit ManagedGreeterService config:propset salutation "Bonjour" config:propset name "Sully" config:updateAnd then update the configuration with new values again:
config:edit ManagedGreeterService config:propset salutation "Bonjour" config:propset name "Sully" config:updateNow lets take a look at our log to see how the components behaved:
INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeeter Service INFO | vent: pid=ManagedGreeterService) | Activating the Managed Greeter Component INFO | pool-22-thread-1 | Message 1: Hello Scott INFO | pool-22-thread-1 | Message 2: Hello Scott INFO | opt/karaf/apache-karaf-2.2.9/etc | Installed /opt/karaf/apache-karaf-2.2.9/etc/ManagedGreeterService.cfg INFO | pool-22-thread-1 | Message 3: Hello Scott INFO | pool-22-thread-1 | Message 4: Hello Scott INFO | pool-22-thread-1 | Message 5: Hello Scott ... INFO | vent: pid=ManagedGreeterService) | Modifying the Managed Greeeter Service INFO | pool-22-thread-1 | Message 11: Bonjour Sully INFO | pool-22-thread-1 | Message 12: Bonjour Sully INFO | pool-22-thread-1 | Message 13: Bonjour Sully INFO | pool-22-thread-1 | Message 14: Bonjour Sully INFO | pool-22-thread-1 | Message 15: Bonjour SullyNow this output should cause concern. Why? Because our components were never deactivated and threads were updated when the component was modified. We are no longer thread safe!
So what happened? Before when we only had the activate & deactivate method defined the activate & deactivate life-cycles were used to manage all updates of the component. This allowed our components to de/activate cleanly. When the component is configured with the modified life-cycle the SCR will only call the modified method which never causes the ManagedGreeterService to become UNSATISFIED. Therefore any component with a dependency on the ManagedGreeterService is never notified that there has been a change in the state of the component and they continue upon their merry way oblivious to the changes.
WRT thread safety, the same actually goes for our set/unset methods as well in the ManagedGreeterComponent. These methods along with the life-cycle methods are called by separate threads in the SCR. If you remember back in Part 1 in my Editorial Opinion, I touched on the importance of creating bind/unbind methods to allow our components to lock be thread safe. If you look at the ManagedGreeterComponent source code you will see where we make sure to wrap our service in lock blocks to ensure that it maintain thread safety.
That should give you a pretty good overview of Managed Services using DS in Karaf. In Part 4 of this series, we will take a look at using components to configure components use DS Factory components.
Next Article:
Part 4: Component Factories
Previous Articles:
Part 1: Getting Started
Part 2: The Basics
References:
Karaf
Felix File Install
OSGi 4.2 Specification
BND Annotations
Monday, September 10, 2012
Declarative Services with Karaf Part 2: The Basics
Now that you have had the chance to install the SCR Commands and the Basic Service Example lets start to look at the code that was executed. The code for the basic GreeterService example is on Github. Follow the steps below to retrieve and build it.
Source Checkout and Build
SCR POM
First lets examine what is required to make our build work. After changing to the service project open the pom.xml in your favorite editor. It is a standard Maven OSGi build configuration using the maven-bundle-plugin with the pom.xml packaging set to bundle. To use the the SCR annotations though we need to add the BND Library. We will also add the Felix SCR Library though in reality we don't need it to build in the case of the GreeterService as it sits. It is only required when your code needs to use the lower level SCR APIs which we will use as we get further along.
Maven Dependency Configuration
Looking over the Maven Bundle Plugin there is a new addition to our configurations that will help create our SCR XML configuration for our bundle. It is the <Service-Component> element. This element tells the BND library to search through our source code and when the @Component annotation is found, create a new configuration for it.
Maven Bundle Plugin Configuration
SCR Sources
Our sources for SCR are annotated POJOs. In the case of the basic GreeterService example detailed below there are two types of Components defined: an Immediate Component (a service consumer) and a Delayed Component (a service provider).
First lets look at the GreeterService. GreeterService.java is a simple interface that provides a single method of invocation.
GreeterService.java
The implementation for the GreeterService is found in GreeterServiceImpl.java.
GreeterServiceImpl.java
This service provider is a very simple implementation that will print a greeting to the log upon activation. The key to this POJO is the inclusion of the @Component annotation at the top of implementation class. As the source code is parsed by BND, the annotations are then used to create the SCR configuration XML that is then included in the makes decisions by what is available as it parses the source code.
In this case the GreeterService is considered a Delayed Component. As BND parses the source code will first consider a Java class with @Component to be a Delayed Component if the class implements an interface. The containers SCR instance will then register the Component and not activate it until the service is required by a service consumer.
If your service needs to be realized immediately you can do so by adding the immediate attribute set to true on the @Component annotation. For instance if you want to activate a component that does implement an interface but isn't considered a service you will need to add this attribute.
Back in Part 1 you may remember that the GreeterService was in an Active state after it was installed. This was due to there being an active consumer of the service in the container, the GreeterComponent. Since the GreeterComponent was in an Active state, the containers SCR instance created an instance of the GreeterService and injected into our GreeterComponent. To understand what happend lets take a look at GreeterComponent.java.
GreeterComponent.java
The GreeterComponent.java class is our service consumer class. It is annotated with the @Component annotation just like GreeterService but considered an Immediate Component. BND will configure GreeterComponent.java as an Immediate Component because it doesn't implement an interface. Therefore once all of its required dependencies are available this component will be activated.
Again, this can be overridden if you need to by adding the immediate attribute set to false on the @Component annotation.
The GreeterComponent also introduces 3 more annotations: @Activate, @Deactivate & @Reference.
The @Reference annotation is used to configure a dependency requirement for a component, in this case the GreeterService. As such the defaults will cause the GreeterComponent to remain deactivated if the GreeterService is not available.
When the GreeterService dependency becomes satisfied, the SCR service will call the configured activation method denoted by the @Activate annotation. Conversely if the dependency becomes unsatisfied post activation the SCR service will call the components configured deactivate method denoted by the @Deactivate annotation.
Running the Example
Open up your instance of Karaf where we installed the Karaf SCR Components in Part 1 so you can manipulate the example components. Once at the Karaf CLI, execute the scr:list command:
Both components are current active as all dependencies are satisfied.
What happens though when the GreeterService is deactivated?
As you can see from above the GreeterComponent becomes unsatisfied. If you review the log file it will show that the GreeterComponents deactivate method has also been called:
Now that it is deactivated lets activate the GreeterService again using scr:activate GreeterComponent. Executing scr:list displays all components are now active.
The log file also shows that the GreeterComponent's activate method was called once its dependencies became satisfied.
Lets see the difference though when we deactivate the GreeterService:
With the GreeterService now disabled the GreeterComponent becomes unsatisfied resulting in the GreeterComponent deactivate method being called.
Modifying and Running Your Own Example
Now that we have covered the basics feel free to review other options that are a part of the BND/OSGi Annotations to the example to see how SCR behaves in Karaf. By now, if you haven't used SCR before, I am sure you are starting to see the benefits and the possiblities. In Part 3 we will start looking at the easiest ManagedServices you have ever developed for OSGi.
Next Article:
Part 3: Managed Services
Previous Articles:
Part 1: Getting Started
References:
Maven Bundle Plugin: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html
BND: http://www.aqute.biz/Bnd/Components
Source Checkout and Build
git clone https://github.com/sully6768/karaf-scr-examples.git cd karaf-scr-examples git fetch mvn clean install cd service
SCR POM
First lets examine what is required to make our build work. After changing to the service project open the pom.xml in your favorite editor. It is a standard Maven OSGi build configuration using the maven-bundle-plugin with the pom.xml packaging set to bundle. To use the the SCR annotations though we need to add the BND Library. We will also add the Felix SCR Library though in reality we don't need it to build in the case of the GreeterService as it sits. It is only required when your code needs to use the lower level SCR APIs which we will use as we get further along.
Maven Dependency Configuration
<dependencies> <!-- Contains the SCR Annotations --> <dependency> <groupId>biz.aQute</groupId> <artifactId>bndlib</artifactId> <version>1.50.0</version> </dependency> <!-- Contains the SCR binaries. Only required if you need the low level SCR APIs <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.apache.felix.scr</artifactId> <version>1.6.0</version> </dependency> --> <!-- Snipped. See example for full config --> </dependencies>
Looking over the Maven Bundle Plugin there is a new addition to our configurations that will help create our SCR XML configuration for our bundle. It is the <Service-Component> element. This element tells the BND library to search through our source code and when the @Component annotation is found, create a new configuration for it.
Maven Bundle Plugin Configuration
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> <instructions> <Service-Component>*</Service-Component> </instructions> </configuration> </plugin>
SCR Sources
Our sources for SCR are annotated POJOs. In the case of the basic GreeterService example detailed below there are two types of Components defined: an Immediate Component (a service consumer) and a Delayed Component (a service provider).
First lets look at the GreeterService. GreeterService.java is a simple interface that provides a single method of invocation.
GreeterService.java
public interface GreeterService { void printGreetings(); }
The implementation for the GreeterService is found in GreeterServiceImpl.java.
GreeterServiceImpl.java
@Component public class GreeterServiceImpl implements GreeterService { private static final Logger LOG = LoggerFactory.getLogger(GreeterServiceImpl.class); private String name = System.getProperty("user.name", "Scott ES"); private String salutation = "Hello"; public void printGreetings() { LOG.info(salutation + " " + name); } }
This service provider is a very simple implementation that will print a greeting to the log upon activation. The key to this POJO is the inclusion of the @Component annotation at the top of implementation class. As the source code is parsed by BND, the annotations are then used to create the SCR configuration XML that is then included in the makes decisions by what is available as it parses the source code.
In this case the GreeterService is considered a Delayed Component. As BND parses the source code will first consider a Java class with @Component to be a Delayed Component if the class implements an interface. The containers SCR instance will then register the Component and not activate it until the service is required by a service consumer.
If your service needs to be realized immediately you can do so by adding the immediate attribute set to true on the @Component annotation. For instance if you want to activate a component that does implement an interface but isn't considered a service you will need to add this attribute.
Back in Part 1 you may remember that the GreeterService was in an Active state after it was installed. This was due to there being an active consumer of the service in the container, the GreeterComponent. Since the GreeterComponent was in an Active state, the containers SCR instance created an instance of the GreeterService and injected into our GreeterComponent. To understand what happend lets take a look at GreeterComponent.java.
GreeterComponent.java
@Component(name = GreeterComponent.COMPONENT_NAME) public class GreeterComponent { public static final String COMPONENT_NAME = "GreeterComponent"; public static final String COMPONENT_LABEL = "Greeter Component"; private static final Logger LOG = LoggerFactory.getLogger(GreeterComponent.class); private GreeterService greeterService; /** * Called when all of the SCR Components required dependencies have been * satisfied. */ @Activate public void activate() { LOG.info("Activating the " + COMPONENT_LABEL); greeterService.printGreetings(); } /** * Called when any of the SCR Components required dependencies become * unsatisfied. */ @Deactivate public void deactivate() { LOG.info("Deactivating the " + COMPONENT_LABEL); } @Reference public void setGreeterService(final GreeterService greeterService) { this.greeterService = greeterService; } public void unsetGreeterService(final GreeterService greeterService) { this.greeterService = null; } }
The GreeterComponent.java class is our service consumer class. It is annotated with the @Component annotation just like GreeterService but considered an Immediate Component. BND will configure GreeterComponent.java as an Immediate Component because it doesn't implement an interface. Therefore once all of its required dependencies are available this component will be activated.
Again, this can be overridden if you need to by adding the immediate attribute set to false on the @Component annotation.
The GreeterComponent also introduces 3 more annotations: @Activate, @Deactivate & @Reference.
The @Reference annotation is used to configure a dependency requirement for a component, in this case the GreeterService. As such the defaults will cause the GreeterComponent to remain deactivated if the GreeterService is not available.
When the GreeterService dependency becomes satisfied, the SCR service will call the configured activation method denoted by the @Activate annotation. Conversely if the dependency becomes unsatisfied post activation the SCR service will call the components configured deactivate method denoted by the @Deactivate annotation.
Running the Example
Open up your instance of Karaf where we installed the Karaf SCR Components in Part 1 so you can manipulate the example components. Once at the Karaf CLI, execute the scr:list command:
karaf@root> scr:list ID State Component Name [7 ] [ACTIVE ] GreeterComponent [6 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>;
Both components are current active as all dependencies are satisfied.
What happens though when the GreeterService is deactivated?
karaf@root> scr:deactivate GreeterComponent karaf@root> scr:list ID State Component Name [-1 ] [DISABLED ] GreeterComponent [9 ] [REGISTERED ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
As you can see from above the GreeterComponent becomes unsatisfied. If you review the log file it will show that the GreeterComponents deactivate method has also been called:
INFO | Deactivating the GreeterService Component
Now that it is deactivated lets activate the GreeterService again using scr:activate GreeterComponent. Executing scr:list displays all components are now active.
karaf@root> scr:activate GreeterComponent karaf@root> scr:list ID State Component Name [7 ] [ACTIVE ] GreeterComponent [9 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
The log file also shows that the GreeterComponent's activate method was called once its dependencies became satisfied.
INFO | Activating the GreeterService Component INFO | Hello sully6768
Lets see the difference though when we deactivate the GreeterService:
karaf@root> scr:deactivate org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root> scr:list ID State Component Name [11 ] [UNSATISFIED ] GreeterComponent [-1 ] [DISABLED ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
With the GreeterService now disabled the GreeterComponent becomes unsatisfied resulting in the GreeterComponent deactivate method being called.
Modifying and Running Your Own Example
Now that we have covered the basics feel free to review other options that are a part of the BND/OSGi Annotations to the example to see how SCR behaves in Karaf. By now, if you haven't used SCR before, I am sure you are starting to see the benefits and the possiblities. In Part 3 we will start looking at the easiest ManagedServices you have ever developed for OSGi.
Next Article:
Part 3: Managed Services
Previous Articles:
Part 1: Getting Started
References:
Maven Bundle Plugin: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html
BND: http://www.aqute.biz/Bnd/Components
Declarative Services with Karaf Part 1: Getting Started
OSGi Declarative Services (DS) is a lightweight and easy to use API that I found makes a very suitable dependency injection (DI) framework for OSGi containers. It has been available as part of the OSGi Specification since 4.0 making it, in my opinion, a very well baked and stable framework. The DS API that provides this capability is called the Service Component Runtime or SCR.
Now DS, like other DI frameworks, uses XML for the wiring at run-time. DS also has annotations available like the other DI frameworks with one big difference; DS Annotations generate the XML at build-time where the other frameworks interpret them at run-time. This capability is provided by using either the BND or Felix Annotations with their respective Maven plugins. Both offerings are solid and stable implementations but have some significant differences:
Felix SCR Annotations
I personally prefer the BND/OSGi annotations. They are lighter weight with regards to development and deployment. I can use my current Maven Bundle Plugin configuration and not have to introduce a new plugin and configuration. I also don't have to install the Felix Annotations into my container. Finally, with Felix SCR Annotations my source no longer matches the generated binary when you allow it to generate the service bindings.
Editorial Opinion: My concerns above are mostly trivial with the exception of the generated code. Even if you use the Felix annotations I would avoid using this capability. The SCR dynamically assigns concrete references to your component (not Proxies, another big plus of DS over other DI frameworks) making it necessary to account for thread safety on these references. Having the binding methods in the codebase allows the use of ReadWriteLocks that can be used across operations which is far superior to making synchronized methods or large synchronization blocks. More on that as we get to Part 3.
The one thing I would love to have though is inheritance. That is still a big negative but it is being discussed over at the OSGi group.
You can find more details about both sets of SCR Annotations in the following locations:
Installing Karaf SCR
To get started first install Karaf 2.2.9. It can be found here:
Now install the Karaf SCR Feature. This will allow you to manipulate the installed examples at runtime.
Navigate to where you installed Karaf 2.2.9 and open the etc/org.ops4j.pax.url.mvn.cfg file. This is where the configuration for Maven repository lookups are kept. Navigate to the bottom of the file and add the following repository:
Once it is up and running we can add the SCR Feature:
You are up and running and ready to start working with SCR Components in Karaf.
Using the Karaf Commands
Now lets go back and explore how to use the Karaf SCR Commands that were installed in Part 1. With the commands and the first example installed lets step through the commands.
scr:list [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:list command will list the current components installed in Karaf.
Note: The most recent version of the Karaf SCR Components now supports hidden components in the CLI. A component with a property of "hidden.component=true" will not be displayed or available for completion without specifying the -s or --show-all option.
If we pass the -s or --show-all options to the scr:list command it will display all of the components in the container including those that provide the command and management functionality for Karaf SCR.
scr:details [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:details command will list the current configuration and state of a given component. Taking a look at the GreeterComponent we see it is currently active and that it has a satisfied reference to to the GreeterService:
scr:deactivate [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:deactivate command changes a component to disabled. Any component with a dependency on the deactivated component will become unsatisfied and deactivate as well.
scr:activate [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:activate command changes a component to enabled. Any component with a dependency on the activated component will become satisfied and activate as well.
In our next article we will start to look at the code behind the components: The Basics.
Next:
Part 2: The Basics
References:
OSGi 4.2 Specifications: http://www.osgi.org/Specifications/HomePage
OSGi 4.2 Javadoc: http://www.osgi.org/javadoc/r4v42/
Apache Felix Maven SCR - http://felix.apache.org/site/apache-felix-maven-scr-plugin.html
BND Annotation - http://www.aqute.biz/Bnd/Components
Now DS, like other DI frameworks, uses XML for the wiring at run-time. DS also has annotations available like the other DI frameworks with one big difference; DS Annotations generate the XML at build-time where the other frameworks interpret them at run-time. This capability is provided by using either the BND or Felix Annotations with their respective Maven plugins. Both offerings are solid and stable implementations but have some significant differences:
Felix SCR Annotations
- Inheritance Support for abstract components
- Generated bind/unbind methods (less code by you, more code by manipulation)
- Boilerplate for the new OSGi DS Annotations in the 4.3 Release
- Fully supported by the Maven Bundle Plugin
- No other library dependencies (Felix requires the annotation lib be installed)
I personally prefer the BND/OSGi annotations. They are lighter weight with regards to development and deployment. I can use my current Maven Bundle Plugin configuration and not have to introduce a new plugin and configuration. I also don't have to install the Felix Annotations into my container. Finally, with Felix SCR Annotations my source no longer matches the generated binary when you allow it to generate the service bindings.
Editorial Opinion: My concerns above are mostly trivial with the exception of the generated code. Even if you use the Felix annotations I would avoid using this capability. The SCR dynamically assigns concrete references to your component (not Proxies, another big plus of DS over other DI frameworks) making it necessary to account for thread safety on these references. Having the binding methods in the codebase allows the use of ReadWriteLocks that can be used across operations which is far superior to making synchronized methods or large synchronization blocks. More on that as we get to Part 3.
The one thing I would love to have though is inheritance. That is still a big negative but it is being discussed over at the OSGi group.
You can find more details about both sets of SCR Annotations in the following locations:
- Apache Felix Maven SCR - http://felix.apache.org/site/apache-felix-maven-scr-plugin.html
- BND Annotation - http://www.aqute.biz/Bnd/Components
Installing Karaf SCR
To get started first install Karaf 2.2.9. It can be found here:
Now install the Karaf SCR Feature. This will allow you to manipulate the installed examples at runtime.
Navigate to where you installed Karaf 2.2.9 and open the etc/org.ops4j.pax.url.mvn.cfg file. This is where the configuration for Maven repository lookups are kept. Navigate to the bottom of the file and add the following repository:
- http://sully6768.github.com/repos/releases/
org.ops4j.pax.url.mvn.repositories= \ http://repo1.maven.org/maven2, \ http://repository.apache.org/content/groups/snapshots-group@snapshots@noreleases, \ http://svn.apache.org/repos/asf/servicemix/m2-repo, \ http://repository.springsource.com/maven/bundles/release, \ http://repository.springsource.com/maven/bundles/external, \ http://oss.sonatype.org/content/repositories/releases/, \ http://sully6768.github.com/repos/releases/Now start Karaf:
./bin/karaf debug karaf@root>
Once it is up and running we can add the SCR Feature:
features:addurl mvn:org.apache.karaf.scr/org.apache.karaf.scr.feature/2.2.9/xml/features features:install scrAfter installing the SCR feature you can verify everything installed correctly by typing scr: in the shell and then hitting the TAB key. This will display the new commands that were installed:
karaf@root> scr: scr:activate scr:deactivate scr:details scr:listFinally, lets install the first SCR example and verify the commands are working as expected:
karaf@root> install -s mvn:org.apache.karaf.scr/org.apache.karaf.scr.examples.service/2.2.9 Bundle ID: 55Now if we execute the scr:list command we should see two componets listed: The GreeterServiceComponent and he GreeterServiceImpl:
karaf@root> scr:list ID State Component Name [6 ] [ACTIVE ] GreeterServiceComponent [5 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl
You are up and running and ready to start working with SCR Components in Karaf.
Using the Karaf Commands
Now lets go back and explore how to use the Karaf SCR Commands that were installed in Part 1. With the commands and the first example installed lets step through the commands.
scr:list [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:list command will list the current components installed in Karaf.
karaf@root> scr:list ID State Component Name [5 ] [ACTIVE ] GreeterComponent [6 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
Note: The most recent version of the Karaf SCR Components now supports hidden components in the CLI. A component with a property of "hidden.component=true" will not be displayed or available for completion without specifying the -s or --show-all option.
If we pass the -s or --show-all options to the scr:list command it will display all of the components in the container including those that provide the command and management functionality for Karaf SCR.
karaf@root> scr:list -s ID State Component Name [5 ] [ACTIVE ] GreeterComponent [1 ] [ACTIVE ] ActivateCommand [2 ] [ACTIVE ] ListCommand [6 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl [4 ] [ACTIVE ] ScrServiceMBean [3 ] [ACTIVE ] DetailsCommand [0 ] [ACTIVE ] DeactivateCommand karaf@root>
scr:details [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:details command will list the current configuration and state of a given component. Taking a look at the GreeterComponent we see it is currently active and that it has a satisfied reference to to the GreeterService:
karaf@root> scr:details GreeterComponent Component Details Name : GreeterComponent State : ACTIVE Properties : ACTIVE component.name=GreeterComponent component.id=5 References Reference : greeterService State : satisfied Multiple : single Optional : mandatory Policy : static Service Reference : Bound Service ID 195 (org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl) karaf@root>
scr:deactivate [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:deactivate command changes a component to disabled. Any component with a dependency on the deactivated component will become unsatisfied and deactivate as well.
karaf@root> scr:deactivate GreeterComponent karaf@root> scr:list ID State Component Name [-1 ] [DISABLED ] GreeterComponent [6 ] [REGISTERED ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
scr:activate [-s | --show-all]
-s and --show-all: Present all components (including hidden) when hitting the tab key
The scr:activate command changes a component to enabled. Any component with a dependency on the activated component will become satisfied and activate as well.
karaf@root> scr:activate GreeterComponent karaf@root> scr:list ID State Component Name [7 ] [ACTIVE ] GreeterComponent [6 ] [ACTIVE ] org.apache.karaf.scr.examples.service.impl.GreeterServiceImpl karaf@root>
In our next article we will start to look at the code behind the components: The Basics.
Next:
Part 2: The Basics
References:
OSGi 4.2 Specifications: http://www.osgi.org/Specifications/HomePage
OSGi 4.2 Javadoc: http://www.osgi.org/javadoc/r4v42/
Apache Felix Maven SCR - http://felix.apache.org/site/apache-felix-maven-scr-plugin.html
BND Annotation - http://www.aqute.biz/Bnd/Components
Declarative Services with Karaf
Since I use OSGi Declarative Services (DS), the associated DS Annotations and some SCR Commands for Karaf that I developed (to be included in Karaf 2.3 & 3.0) for my example projects I figured I should have a primer on their use with some example SCR code.
Part 1: Getting Started
Part 2: The Basics
Part 3: Managed Services
Part 4: Component Factories
Enjoy!
Part 1: Getting Started
Part 2: The Basics
Part 3: Managed Services
Part 4: Component Factories
Enjoy!
Adding a Shell Alias to Karaf
Did you know you can add a shell alias to Karaf just like you can in your favorite bash shell? Its pretty simple. Just follow the steps below to add ll (list):
- Navigate to your installation of Karaf
- Open etc/shell.init.script
- After the license header add the following:ll = { osgi:list $args } ;
Now restart and ll will be available to you at the shell.
There are already a number of available ones installed so take a look and see what works for you.
Subscribe to:
Posts (Atom)