Chapter 12. Service Component Runtime

Abstract

Service Component Runtime (SCR) is an implementation of OSGi Declarative Services specification. It enables any plain old Java object to expose and use OSGi services with no boilerplate code.

Working with Camel and SCR

Apache Camel SCR component provides an integration of Camel with the OSGi Service Component Runtime.
OSGi framework has ability to know the object by looking at SCR descriptor file in its bundle which are generated from Java annotations by a plugin such as org.apache.felix:maven-scr-plugin. Using SCR, the bundle remains completely in Java world. There is no need to edit XML or properties file. It offers you full control over the project.

Creating a Service Component

Following are the steps to create a service component out of your java class.
  1. Add the required org.apache.felix.scr.annotations at class level. For example,
    @Component
    @References({
        @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class,
            cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC,
            policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent")
    })
    
    
    
  2. Implement the getRouteBuilders() method that returns the Camel route you want to run. For example,
    @Override
    protected List<RoutesBuilder> getRouteBuilders() {
        List<RoutesBuilder> routesBuilders = new ArrayList<>();
        routesBuilders.add(new YourRouteBuilderHere(registry));
        routesBuilders.add(new AnotherRouteBuilderHere(registry));
        return routesBuilders;
    }
    
    
  3. Finally, enter the default configuration in annotations.
    @Properties({
       @Property(name = "camelContextId", value = "my-test"),
       @Property(name = "active", value = "true"),
       @Property(name = "...", value = "..."),
       ...
    })
    
    

Example of a Service Component class

The following example illustrates the use of camel-archetype-scr to generate a complete service component class:
// This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT
package example;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.camel.scr.AbstractCamelRunner;
import example.internal.CamelScrExampleRoute;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.spi.ComponentResolver;
import org.apache.felix.scr.annotations.*;
 
@Component(label = CamelScrExample.COMPONENT_LABEL, description = CamelScrExample.COMPONENT_DESCRIPTION, immediate = true, metatype = true)
@Properties({
    @Property(name = "camelContextId", value = "camel-scr-example"),
    @Property(name = "camelRouteId", value = "foo/timer-log"),
    @Property(name = "active", value = "true"),
    @Property(name = "from", value = "timer:foo?period=5000"),
    @Property(name = "to", value = "log:foo?showHeaders=true"),
    @Property(name = "messageOk", value = "Success: {{from}} -> {{to}}"),
    @Property(name = "messageError", value = "Failure: {{from}} -> {{to}}"),
    @Property(name = "maximumRedeliveries", value = "0"),
    @Property(name = "redeliveryDelay", value = "5000"),
    @Property(name = "backOffMultiplier", value = "2"),
    @Property(name = "maximumRedeliveryDelay", value = "60000")
})
@References({
    @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class,
        cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC,
        policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent")
})
public class CamelScrExample extends AbstractCamelRunner {
 
    public static final String COMPONENT_LABEL = "example.CamelScrExample";
    public static final String COMPONENT_DESCRIPTION = "This is the description for camel-scr-example.";
 
    @Override
    protected List<RoutesBuilder> getRouteBuilders() {
        List<RoutesBuilder> routesBuilders = new ArrayList<>();
        routesBuilders.add(new CamelScrExampleRoute(registry));
        return routesBuilders;
    }
}

Example of a RouteBuilder class

The following example illustrates the use of camel-archetype-scr to generate a RouteBuilder class:
// This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT
package example.internal;
 
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.SimpleRegistry;
import org.apache.commons.lang.Validate;
 
public class CamelScrExampleRoute extends RouteBuilder {
 
    SimpleRegistry registry;
 
    // Configured fields
    private String camelRouteId;
    private Integer maximumRedeliveries;
    private Long redeliveryDelay;
    private Double backOffMultiplier;
    private Long maximumRedeliveryDelay;
 
    public CamelScrExampleRoute(final SimpleRegistry registry) {
        this.registry = registry;
    }
 
    @Override
    public void configure() throws Exception {
        checkProperties();
 
        // Add a bean to Camel context registry
        registry.put("test", "bean");
 
        errorHandler(defaultErrorHandler()
            .retryAttemptedLogLevel(LoggingLevel.WARN)
            .maximumRedeliveries(maximumRedeliveries)
            .redeliveryDelay(redeliveryDelay)
            .backOffMultiplier(backOffMultiplier)
            .maximumRedeliveryDelay(maximumRedeliveryDelay));
 
        from("{{from}}")
            .startupOrder(2)
            .routeId(camelRouteId)
            .onCompletion()
                .to("direct:processCompletion")
            .end()
            .removeHeaders("CamelHttp*")
            .to("{{to}}");
 
 
        from("direct:processCompletion")
            .startupOrder(1)
            .routeId(camelRouteId + ".completion")
            .choice()
                .when(simple("${exception} == null"))
                    .log("{{messageOk}}")
                .otherwise()
                    .log(LoggingLevel.ERROR, "{{messageError}}")
            .end();
        }
    }
 
    public void checkProperties() {
        Validate.notNull(camelRouteId, "camelRouteId property is not set");
        Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set");
        Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set");
        Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set");
        Validate.notNull(maximumRedeliveryDelay, "maximumRedeliveryDelay property is not set");
    }
}

Using Apache Camel SCR bundle as a template

If you have a Camel SCR bundle that implements an integration pattern that you use frequently, then, probably there is no need to create a separate bundle for every instance.
The following example illustrates how to use a Camel SCR bundle as a template. It includes the following steps:
  1. Create a configuration PID for your service component and add a tail with a dash.
  2. Camel SCR will use the configuration to create a new instance of your component.
  3. Finally, you can start a new CamelContext with your overridden properties.
    # Create a PID with a tail
    karaf@root> config:edit example.CamelScrExample-anotherone
     
    # Override some properties
    karaf@root> config:propset camelContextId my-other-context
    karaf@root> config:propset to "file://removeme?fileName=removemetoo.txt"
     
    # Save the PID
    karaf@root> config:update
    Note
    Make sure that your service component does not start with the default configuration. To prevent this, add policy = ConfigurationPolicy.REQUIRE to the class level at component annotation.

Using Apache camel-archetype-scr

With the help of Apache camel-archetype-scr and maven, you can easily create Apache Camel SCR bundle project. It includes the following steps:
  1. Run the following command:
    $ mvn archetype:generate -Dfilter=org.apache.camel.archetypes:camel-archetype-scr
     
    Choose archetype:
    local -> org.apache.camel.archetypes:camel-archetype-scr (Creates a new Camel SCR bundle project for Karaf)
    Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
    Define value for property 'groupId': : example
    [INFO] Using property: groupId = example
    Define value for property 'artifactId': : camel-scr-example
    Define value for property 'version': 1.0-SNAPSHOT: :
    Define value for property 'package': example: :
    [INFO] Using property: archetypeArtifactId = camel-archetype-scr
    [INFO] Using property: archetypeGroupId = org.apache.camel.archetypes
    [INFO] Using property: archetypeVersion = 2.15-SNAPSHOT
    Define value for property 'className': : CamelScrExample
    Confirm properties configuration:
    groupId: example
    artifactId: camel-scr-example
    version: 1.0-SNAPSHOT
    package: example
    archetypeArtifactId: camel-archetype-scr
    archetypeGroupId: org.apache.camel.archetypes
    archetypeVersion: 2.15-SNAPSHOT
    className: CamelScrExample
    Y: :
    
    
  2. Run Apache Maven.
    Note
    For details on setting up Apache Maven to work with Red Hat JBoss Fuse, see Building with Maven in Red Hat JBoss Fuse Deploying into the Container on the Red Hat Customer Portal
  3. You can now deploy the bundle. To deploy the bundle on Apache Karaf, perform the following steps on Karaf command line:
    # Add Camel feature repository
    karaf@root> features:chooseurl camel 2.15-SNAPSHOT
     
    # Install camel-scr feature
    karaf@root> features:install camel-scr
     
    # Install commons-lang, used to validate parameters
    karaf@root> osgi:install mvn:commons-lang/commons-lang/2.6
     
    # Install and start your bundle
    karaf@root> osgi:install -s mvn:example/camel-scr-example/1.0-SNAPSHOT
     
    # View the log.
    karaf@root> log:tail -n 10
     
    Press ctrl-c to stop the log.
    
    
    
  4. By default, the Service Component's configuration PID equals the fully qualified name of its class. You can change the properties of a bundle with Apache Karaf's config.* commands:
    # Override the messageOk property
    karaf@root> config:propset -p example.CamelScrExample messageOk "This is better logging".
    
    
    You can also change the configuration by editing the property file in Apache Karaf's etc directory.