Carbon 5 Kernel Architecture

This post will describe the current architecture of Carbon 5 Kernel.

C5-Kernel-Architecture

The kernel runs on top of an OSGi framework. Eclipse Equinox is the currently used OSGi run-time framework. But the kernel has support for using any OSGi framework underneath.

Carbon 5 Kernel has a set core modules which expose some well defined API’s as services. These set of modules are as follows.

Clustering Framework

The Clustering Framework provides the clustering feature for carbon kernel which adds support for High Availability, Scalabilty and Failover

Artifact Deployment Framework

The deployment framework is responsible for managing the deployment and undeployment of artifacts in a carbon server. This also manages the Deployers (WebappDeployer, ServiceDeployers, etc), which gets plugged in to the Deployment Engine with a runtime which is explained next.

Pluggable runtime framework.

This module is responsible for handling and managing integrated 3rd party Runtime’s, such as Tomcat, Axis2, Synapse on Carbon server.

User Management

This module provides the functionality for user management (Authentication and Authorization) aspect for a carbon server.

Logging

A centralized logging framework provide logging functionality to the kernel, with the support for all the commonly used logging API’s.

Multi Tenancy & Hierarchical Tenancy

Carbon Multi Tenancy (Hierarchical) module provides the multi-tenancy aspect to carbon platform both in execution level and at OSGi level. Each and every tenant deployed in a single JVM will get his own OSGi isolated region. This would ensure class space, bundles and OSGi service level isolation for tenants and also provide application-level isolation for tenants.

Configuration & Context

The Carbon Configuration & Context model allows CarbonRuntime implementations to retrieve the configuration instance. CarbonRuntime represents the complete server space and CarbonContext is the entity which provides the CarbonRuntime related contextual information of the current executing thread. This module will also provide a model for the CarbonConfiguration (carbon.xml) to runtime.

Advertisements
Posted in Carbon5, WSO2 | Tagged , , , | Leave a comment

Carbon Kernel 5.0.0 Milestone-03 is released

Carbon 5 Kernel Milestone-03 is now released. It can be downloaded from here.

Here is the release note for this milestone release.

Carbon 5 [C5] is the next generation of WSO2 Carbon platform. The existing Carbon platform has served as a modular middleware platform for more than 5 years now. We’ve built many different products, solutions based on this platform. All the previous major releases of Carbon were sharing the same high level architecture, even though we’ve changed certain things time to time.

Base architecture of the Carbon is modeled using the Apache Axis2’s kernel architecture. Apache Axis2 is Web service engine. But it also has introduced a rich extensible server framework with a configuration and runtime model, deployment engine, clustering API and a implementation, etc. We extended this architecture and built a OSGI based modular server development framework called Carbon Kernel. It is tightly coupled with Apache Axis2. But now Apache Axis2 is becoming a dead project. We don’t see enough active development on the trunk. Therefore we thought of getting rid of this tight coupling to Apache Axis2.

Carbon kernel has gained weight over the time. There are many unwanted modules there. When there are more modules, the rate of patching or the rate of doing patch releases increases. This is why we had to release many patch releases of Carbon kernel in the past. This can become a maintenance nightmare for developers as well as for the users. We need to minimize Carbon kernel releases.

The other reason for C5 is to make Carbon kernel a general purpose OSGi runtime, specialized in hosting servers. We will implement the bare minimal features required for server developers in the Carbon kernel.

Our primary goal of C5 is to re-architect the Carbon platform from the ground up with the latest technologies and patterns to overcome the existing architectural limitations as well as to get rid of the dependencies to the legacy technologies like Apache Axis2. We need to build a next generation middleware platform that will last for the next 10 years.

This milestone release is a step towards building an OSGi based server development framework. It includes following new features.

New Features

  • Pluggable runtimes framework [Jira] [apidoc]
  • Clustering module  [Jira] [apidoc]
  • Context and config model, hierarchical tenancy model (Experimental)  [Jira] [apidoc]

Key Features

  • Artifact deployment engine [Jira] [apidoc]
  • Centralized logging back-end
  • Carbon launcher framework

Documentation

WSO2 Carbon Kernel 5.0.0 – Documentation

Fixed Issues

WSO2 Carbon Kernel 5.0.0 – Fixed Issues

How To Contribute

You can find more instructions on how to contribute on our documentation site.

If you have any suggestions or interested in C5 discussions, please do so via dev@wso2.org or architecture@wso2.org mailing lists .

Reporting Issues

We encourage you to report issues, documentation faults and feature requests regarding WSO2 C5 through the public issue tracking system.

Posted in Carbon5, WSO2 | Tagged , , , | Leave a comment

Using Annotations with OSGi Declarative Services

Introduction

The OSGi compendium specification has now standardized the annotation usage for declarative services. The core services bundle (org.eclipse.osgi.services) includes the set of classes and packages that can be used for this purpose. The specification clearly mentions that there should be some tools that are capable of processing these annotation and generate the declarative services component descriptor files. The Apache Felix project has developed these tools (maven dependencies and plugins) which can process the standard annotations and generate the descriptor meta files for you. A point to note that Apache Felix project also has its own annotations, based on org.apache.felix.scr.annotations dependency, which has similar names as those mentioned in the specification. The standard annotations specified by the compendium specification are supported by the org.apache.felix.scr.ds-annotations  felix project. This post will focus on the standard annotations and how to use within your project.

Dependencies

The main dependencies and plugins you will be needing are as follows.

<dependency>
     <groupId>org.eclipse.osgi</groupId>
     <artifactId>org.eclipse.osgi.services</artifactId>
</dependency>

<dependency>
     <groupId>org.apache.felix</groupId>
     <artifactId>org.apache.felix.scr.ds-annotations</artifactId>
</dependency>
<plugin>
     <groupId>org.apache.felix</groupId>
     <artifactId>maven-scr-plugin</artifactId>
     <version>1.16.0</version>
     <executions>
          <execution>
               <id>generate-scr-scrdescriptor</id>
               <goals>
                    <goal>scr</goal>
               </goals>
          </execution>
     </executions>
</plugin>

Alternatively using maven-bundle-plugin, you can use the “_dsannotations” instruction to process the annotated declarative service components  and then component descriptors will be generated. An example of using maven-bundle-plugin for this purpose is below.

<plugin>
     <groupId>org.apache.felix</groupId>
     <artifactId>maven-bundle-plugin</artifactId>
     <version>2.4.0</version>
     <extensions>true</extensions>
     <configuration>
          <instructions>
               <Bundle-Vendor>WSO2 Inc</Bundle-Vendor>
               <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
               <Export-Package>
                    org.wso2.carbon.deployment.*;version="5.0.0"
               </Export-Package>
               <Import-Package>
                    org.slf4j.*;version="${slf4j.logging.import.version.range}",
                    org.wso2.carbon.kernel.*;version="${carbon.kernel.package.import.version.range}",
                    org.osgi.framework.*;version="${osgi.framework.import.version.range}"
               </Import-Package>
               <_dsannotations>*</_dsannotations>
          </instructions>
     </configuration>
</plugin>

The available annotations classes are as below, based on the OSGi compendium specification.

  • Activate – Identify the annotated method as the activate method of a Service Component.
  • Component – Identify the annotated class as a Service Component.
  • ConfigurationPolicy – Configuration Policy for the Component annotation.
  • Deactivate – Identify the annotated method as the deactivate method of a Service Component.
  • Modified – Identify the annotated method as the modified method of a Service Component.
  • Reference – Identify the annotated method as a bind method of a Service Component.
  • ReferenceCardinality – Cardinality for the Reference annotation.
  • ReferencePolicy – Policy for the Reference annotation.
  • ReferencePolicyOption – Policy option for the Reference annotation.

Example Usage

Below is an example service component class which uses the standard annotations.

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.servlet.SampleServlet;

import javax.servlet.ServletException;

/**
 * This service  component is responsible for retrieving the HttpService
 * OSGi service and register servlets
 */

@Component(
        name = "org.wso2.carbon.transport.HttpServiceComponent",
        immediate = true
)
public class HttpServiceComponent {

    private static final Logger logger = LoggerFactory.getLogger(HttpServiceComponent.class);

    private HttpService httpService;

    @Activate
    protected void start() {
        SampleServlet servlet = new SampleServlet();
        String context = "/sample";
        try {
            logger.info("Registering a sample servlet : {}", context);
            httpService.registerServlet(context, servlet, null,
                                        httpService.createDefaultHttpContext());
        } catch (ServletException | NamespaceException e) {
            logger.error("Error while registering servlet", e);
        }
    }


    @Reference(
            name = "http.service",
            service = HttpService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.STATIC,
            unbind = "unsetHttpService"
    )
    protected void setHttpService(HttpService httpService) {
        this.httpService = httpService;
    }

    protected void unsetHttpService(HttpService httpService) {
        this.httpService = null;
    }
}

With the usage of maven dependencies and plugin given in the “Usage” section above, the component descriptor will be generated as below. Also if a component class is an implementation class of a service and needed to be registered as a service, then the @Component annotation will automatically populate the component descriptor with that information.

<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
     <scr:component immediate="true" name="org.wso2.carbon.transport.HttpServiceComponent" activate="start">
          <implementation class="org.wso2.carbon.transport.internal.HttpServiceComponent"/>
          <reference name="http.service" interface="org.osgi.service.http.HttpService" cardinality="1..1" policy="static" bind="setHttpService" unbind="unsetHttpService"/>
     </scr:component>
</components>

@Component

This annotation identifies the annotated class as a service component.

@Component(
        name = "ClusteringAgentServiceComponent",
        immediate = true,
        property = "Agent=hazelcast"
)

The component annotation can take multiple parameters like in the above example. Those will be available for that component at run-time. For example, if your component needs to register component or service level properties, then it can be done by using the “property” parameter and with one or many “key=value” pairs.

@Reference

    @Reference(
            name = "http.service",
            service = HttpService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.STATIC,
            unbind = "unsetHttpService"
    )

The @Reference annotation should be applied to a method which will be used as a “bind” method of a service component. You can refer the example class above. The “unbind” parameter specifies the unbind method in the service component, along with other needed parameters for the reference to satisfy.

@Acitvate, @Deactivate, @Modified

The above three annotation are used with the respective methods that will be called when the service component state changes from one to another. For example the @Activate annotated method gets invoked when the service component becomes satisfied with all the service references and their requirements.

Posted in How to, Java, OSGi | Tagged , , , , , , | 2 Comments

OSGi and Jetty Integration

Introduction

If any OSGi framework based solution needs a servlet container, the OSGified Jetty becomes a good choice. It can be used as the underlying servlet transport provider within an OSGI framework.  This post focus on what are major points to consider when integration Jetty within an OSGi environment.

Integration

The main advantage of Jetty is that it is OSGi framework friendly. It has a set of bundles that can be used for this purpose. Most of the details regarding this bundles is available in the documentation. The  minimum set of bundles needed to embed Jetty in your OSGi environment is given in the documentation.

By default Jetty run-time expects jetty configuration files to be available in a predefined location. This is known as the jetty home. This can be set a a java system variable like below.

-Djetty.home=/opt/custom/jetty

The /opt/custom/jetty will contain the jetty related configuration files under “etc” directory.

The default configuration file of jetty is known as the “jetty.xml”. The other files contains specific configuration. But they also can be merged into one single file as jetty.xml and used.

By default a jetty server instance will be started at port 8080. If this value need to be change then it can be done via setting a system property as below.

-Djetty.port=9763

But in most cases the requirement would be to control on how and when to start a jetty instance in your OSGi environment. So to do that what we can do is to avoid the default jetty instance initialization. If you don’t set the jetty.home system variable, then a default jetty instance will not be started. Then using an OSGi bundle + OSGi Service registartion approach you can boot up a Jetty instance like below.

public class JettyBundleActivator implements BundleActivator {

    @Override
    public void start(BundleContext bundleContext) throws Exception {

        String jettyHome = "/opt/jetty";
        Server server = new Server();
        //server configuration goes here
        String serverName = "jetty-server";
        Dictionary serverProps = new Hashtable();
        serverProps.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
        serverProps.put(OSGiServerConstants.JETTY_PORT, "9763");
        serverProps.put(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS,
                        "file:" + jettyHome + File.separator + "jetty.xml");

        //register as an OSGi Service for Jetty to find
        bundleContext.registerService(Server.class.getName(), server, serverProps);
    }
}

The above will register a jetty instance which will be identified by the jetty boot bundle (org.eclipse.jetty.osgi:jetty-osgi-boot) and the instance will be started. By this way, you can take control on the jetty instance boot-up. Like above, any number of jetty instances can be started using the same approach.

The following are the supported features of Jetty in an OSGi environment.

  1. Deploying Bundles as Webapps
  2. Deploying Bundles as Jetty ContextHandlers
  3. Deploying Services as Webapps
  4. Deploying Services as ContextHandlers

Exposing OSGi HttpService

With Jetty integration, the OSGi HttpService can also be exposed for use within the OSGi environment such as servlet registration, etc. An additional bundle is needed in-order to expose HttpService, which includes the HttpService implementation. The equinox http service servlet (HttpServiceServlet) can be used as a HttpService implementation and registered with Jetty for exposing the HttpService. The equinox bundle which can be used for this is org.eclipse.equinox:org.eclipse.equinox.http.servlet.

The below code segment shows the approach on using HttpServiceServlet registration with running Jetty server instance. This code segment can be used within a BundleActivator or a DeclarativeServiceCompoenent.

//exposing the OSGi HttpService by registering the HttpServiceServlet with Jetty.
ServletHolder holder = new ServletHolder(new HttpServiceServlet());
ServletContextHandler httpContext = new ServletContextHandler();

httpContext.addServlet(holder, "/*");
Dictionary servletProps = new Hashtable();
servletProps.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);

bundleContext.registerService(ContextHandler.class.getName(), httpContext, servletProps);

The above will register HttpServiceServlet with Jetty run-time which will expose the HttpService in the OSGi run-time.

Using OSGi HttpService

By referencing the HttpService, users can register new servlets with it. The jetty run-time will discover and route requests to those servlet request to via HttpServiceServlet.

An example for servlet registration with exposed HttpService is given as below.

@Component(
        name = "HttpServiceComponent",
        description = "This service  component is responsible for retrieving the HttpService " +
                      "OSGi service and register servlets",
        immediate = true
)
public class HttpServiceComponent {

    private static final Logger logger = LoggerFactory.getLogger(HttpServiceComponent.class);

    @Reference(
            name = "http.service",
            referenceInterface = HttpService.class,
            cardinality = ReferenceCardinality.MANDATORY_UNARY,
            policy = ReferencePolicy.STATIC,
            bind = "setHttpService",
            unbind = "unsetHttpService"
    )
    private HttpService httpService;

    @Activate
    protected void start() {
        SampleServlet servlet = new SampleServlet();
        String context = "/sample";
        try {
            logger.info("Registering a sample servlet : {}", context);
            httpService.registerServlet(context, servlet, null,
                                        httpService.createDefaultHttpContext());
        } catch (ServletException | NamespaceException e) {
            logger.error("Error while registering servlet", e);
        }
    }

    protected void setHttpService(HttpService httpService) {
        this.httpService = httpService;
    }

    protected void unsetHttpService(HttpService httpService) {
        this.httpService = null;
    }
}
Posted in How to, Java, OSGi | Tagged , , , , , | Leave a comment

Carbon 5.0 Clustering Framework – My Notes

The clustering module provides the clustering feature for carbon kernel which adds support for High Availability, Scalabilty and Failover. The overall architecture of clustering implementation in a single node is given below.

Below are the description of each components of the clustering framework.

ClusterConfiguration – The Cluster Configuration which holds the static information of the cluster. This is will be build and populated using the cluster.xml

ClusterContext – The cluster context which holds the run-time information of the cluster such as members, membership listeners.

ClusteringAgent – The ClusteringAgent which manages the cluster node in a cluster. This will basically do the starting, joining and shutdown the node with cluster. It also provide the functionality to send cluster messages to the cluster, or w set of cluster members in the cluster. Any new clustering implementation that need to be plugged into carbon, should implement this and register it as an OSGi service with the service level property (Agent) to uniquely identify it at run-time.

ClusterService – The cluster service is provided by the cluster framework. It provides a set of functions such as sending cluster message to cluster or to a set of members in the cluster retrieve the current members in the cluster, etc.

MebershipScheme – A representation of a membership scheme such as “multicast based” or “well-known address (WKA) based” schemes. This is directly related to the membership discovery mechanism.

The clustering framework by default supports Multicast or WKA (Well Known Addressing) membership schemes.

  1. Multicast – membership is automatically discovered using multicasting
  2. WKA – Well-Known Address based membership scheme. Membership is discovered with the help of one or more nodes running at a Well-Known Address. New members joining a cluster will first connect to a well-known node, register with the well-known node and get the membership list from it. When new members join, one of the well-known nodes will notify the others in the group. When a member leaves the cluster or is deemed to have left the cluster, it will be detected by the Group Membership Service (GMS) using a TCP ping mechanism.

Clustering Agent

The main part of a clustering implementation is the clustering agent. By default carbon will ship “Hazelcast” based clustering agent implementation. If a new implementation need to be plugged into carbon, it should implement this and then should be registered as an OSGi service with the service level property (Agent) to uniquely identify it at run-time.

Below provides a step by step guide on plugging a new clustering agent implementation (For example : zookeeper coordination framework based clustering agent implementation).

1. Implement the clustering agent interface.

public interface ClusteringAgent {
    /**
     * Initialize the agent which will initialize this node, and join the cluster
     */
    void init(ClusterContext clusterContext) throws ClusterInitializationException;
    /**
     * Shutdown the agent which will remove this node from cluster
     */
    void shutdown();
    /**
     * Send a message to all members in the cluster
     */
    void sendMessage(ClusterMessage msg) throws MessageFailedException;
    /**
     * Send a message to a set of specific members in the cluster
     */
    void sendMessage(ClusterMessage msg, List<ClusterMember> members) throws MessageFailedException;
}

2. Register this as an OSGi service so that the cluster framework will discover this. But the service registration should also provide a service level parameter “Agent” with the meaningful value, where the cluster framework will compare that parameter with the value in cluster.xml to correctly identify the cluster agent implementation at run-time. Service registration can now be done via annotation based approach. In carbon the Apache Felix SCR Annotation plugin is used for this purpose. The above agent class implementation will look like the below after it has been annotated with required annotation.

@Component(
        name = "ZookeeperClusteringAgentServiceComponent",
        description = "The ClusteringAgent class which is based on Zookeeper",
        immediate = true
)
@Service
@Property(name = "Agent", value = "zookeeper")
public class ZookeeperClusteringAgent implements ClusteringAgent {
    @Override
    public void init(ClusterContext clusterContext) throws ClusterInitializationException {
        // Add the logic that should be executed for agent initialization
    }
    @Override
    public void shutdown() {
        // This will be called when the cluster agent bundle/component deactivates.
    }
    @Override
    public void sendMessage(ClusterMessage msg) throws MessageFailedException {
        // This method should implement the logic of sending a message to all members in the cluster.
    }
    @Override
    public void sendMessage(ClusterMessage msg, List<ClusterMember> members)
            throws MessageFailedException {
        // This method should implement the logic of sending a message to specific members in the cluster.
    }
}

Cluster Configration

The clustering can be configured via the cluster.xml for a carbon server. This file is located in $CARBON_HOME/repository/conf/cluster.xml. The following shows the default cluster configuration. To enable clustering, simply change the “Enable” element to “true”, which will enable clustering for that node.

<Cluster xsi:schemaLocation="http://wso2.com/schema/clustering/config cluster.xsd"
         xmlns="http://wso2.com/schema/clustering/config"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <!--Indicates whether clustering should be enabled or disabled for this node-->
    <Enable>false</Enable>
    <!-- The agent implementation used with cluster framework-->
    <Agent>hazelcast</Agent>
    <!--
     The clustering domain/group. Nodes in the same group will belong to the same multicast
     domain. There will not be interference between nodes in different groups.
    -->
    <Domain>wso2.carbon.domain</Domain>
    <!-- Properties specific to this member -->
    <LocalMember>
        <Host>127.0.0.1</Host>
        <Port>4000</Port>
        <Properties>
            <Property name="backendServerUrl">https://${hostName}:${httpsPort}/services/</Property>
            <Property name="mgtConsoleUrl">https://${hostName}:${httpsPort}/</Property>
            <Property name="subDomain">worker</Property>
        </Properties>
    </LocalMember>
    <!--
        The membership scheme used in this setup. The only values supported at the moment are
        "Multicast" and "WKA"
        1. Multicast - membership is automatically discovered using multicasting
        2. WKA - Well-Known Address based multicasting. Membership is discovered with the help
            of one or more nodes running at a Well-Known Address. New members joining a
            cluster will first connect to a well-known node, register with the well-known node
            and get the membership list from it. When new members join, one of the well-known
            nodes will notify the others in the group. When a member leaves the cluster or
            is deemed to have left the cluster, it will be detected by the Group Membership
            Service (GMS) using a TCP ping mechanism.
    -->
    <MembershipScheme>
        <!-- multicast membership scheme related properties-->
        <Multicast>
            <Group>228.0.0.4</Group>
            <Port>45564</Port>
            <TTL>100</TTL>
            <Timeout>60</Timeout>
        </Multicast>
        <!-- wka membership scheme related properties-->
        <!--WKA>
            <Members>
                <Member>
                    <Host>127.0.0.1</Host>
                    <Port>4000</Port>
                </Member>
            </Members>
        </WKA-->
    </MembershipScheme>
</Cluster>

Cluster Service

The Cluster API which is given as an OSGi service to carbon platform will have the following methods. The users can consume this service using OSGi service reference/discovery mechanism.

public interface Cluster {
    /**
     * Send the given cluster message to the whole cluster
     *
     * @param clusterMessage the cluster message to be sent
     * @throws MessageFailedException on error
     */
    void sendMessage(ClusterMessage clusterMessage) throws MessageFailedException;
    /**
     * Send the given cluster message to a set of members in the cluster
     *
     * @param clusterMessage the cluster message to be sent
     * @param members        the list of members to send the cluster message
     * @throws MessageFailedException on error
     */
    void sendMessage(ClusterMessage clusterMessage, List<ClusterMember> members)
            throws MessageFailedException;
    /**
     * Return the list of currently available members in the cluster
     *
     * @return the member list
     */
    List<ClusterMember> getMembers();
}
Posted in Carbon5, WSO2 | Tagged , , , | 1 Comment

Carbon 5.0 Deployment Framework – My Notes

Carbon 5  will be the next generation WSO2 Carbon Platform with a complete new architecture written from scratch. Carbon Kernel is the base of this platform and for all the WSO2 products.

The Carbon Deployment Framework is core a module at kernel for managing the deployment of artifacts in a carbon server.

C5-DeploymentEngine

The above picture illustrates the high level view of this framework. The framework consists of following parts.

  1. Scheduler : is responsible for the scheduling the deployment task periodically.
  2. Repository Scanner : is what does the scanning of deployer directories for artifact updates.
  3. DeployerServiceListener : is an OSGi service component, which will listen to deployer registrations/unregistrations from other components (CustomDeployers) and add/remove them to/from DeploymentEngine at run-time.

The deployment engine will operate at scheduled mode by default, where a scheduler task will run periodically and calls the repository scanner to scans the available deployer directories and find any new artifacts to be deployed, artifact to be updated, and artifact to be undeployed . Then it will call the relevant deployer of those artifacts to do the deployment/undeployment process.
The deployment scan interval is 15 seconds by default. It can be configured using the carbon.xml by changing the following property : DeploymentConfig -> UpdateInterval.
The default repository location for the deployment engine is $CARBON_HOME/repository/deployment/server.

How to write a new Deployer and plug with Deployment Framework

The Deployer SPI provide a way to implement your own deployer which will be used for the deployment of different type of artifacts in carbon. A deployer will process a particular artifact type and deploy it to a run-time configuration. A developer who wants write a deployer to process an artifact in carbon and add it to a runtime configuration, should implement this.

public interface Deployer {

    /**
     * Initialize the Deployer
     *
     * This will contain all the code that need to be called when the deployer is initialized
     */
    void init();

    /**
     * Process a deployable artifact and add it to the relevant runtime configuration
     *
     * @param artifact the Artifact object to deploy
     * @return returns a key to uniquely identify an artifact within a runtime
     * @throws CarbonDeploymentException - when an error occurs while doing the deployment
     */
    Object deploy(Artifact artifact) throws CarbonDeploymentException;

    /**
     * Remove a given artifact from the relevant runtime configuration
     *
     * @param key the key of the deployed artifact used for undeploying it from the relevant runtime
     * @throws CarbonDeploymentException - when an error occurs while running the undeployment
     */
    void undeploy(Object key) throws CarbonDeploymentException;

    /**
     * Updates a already deployed artifact and update its relevant runtime configuration
     *
     * @param artifact the Artifact object to deploy
     * @return returns a key to uniquely identify an artifact within a runtime
     * @throws CarbonDeploymentException - when an error occurs while doing the deployment
     */
    Object update(Artifact artifact) throws CarbonDeploymentException;

    /**
     * Returns the deploy directory location associated with the deployer.
     * It can be relative to CARBON_HOME or an abosolute path
     *      Eg : webapps, dataservices, sequences  or
     *           /dev/wso2/deployment/repository/  or
     *           file:/dev/wso2/deployment/repository/
     *
     * @return deployer directory location
     */
    URL getLocation();

    /**
     * Returns the type of the artifact that the deployer is capable of deploying
     *      Eg : webapp, dataservice
     * @return ArtifactType object which contains info about the artifact type
     */
    ArtifactType getArtifactType();

}

The above is the interface to implement. Lets look at a real example.
As an example on how to write your own deployer and register it with Carbon Deployment Engine, lets look at a simple example deployer which process xml files. This deployer will process xml files found under “xmls” in the default carbon server repository, which is $CARBON_HOME/repository/deployment/server.

1. Create a simple maven project with the following dependecies

<dependency>
    <groupId>org.wso2.carbon</groupId>
    <artifactId>org.wso2.carbon.deployment</artifactId>
    <version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.apache.felix</groupId>
    <artifactId>org.apache.felix.scr.annotations</artifactId>
    <version>1.9.6</version>
</dependency>

2. Implement the Deployer interface. For example like below

public class XMLDeployer implements Deployer {
    private static final Log log = LogFactory.getLog(XMLDeployer.class);
    private ArtifactType artifactType;
    private URL repository;

    @Override
    public void init() {
        log.info("Initializing the XMLDeployer");
        artifactType = new ArtifactType("xml");
        try {
            repository = new URL("file:xmls");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
    @Override
    public String deploy(Artifact artifact) throws CarbonDeploymentException {
        log.info("Deploying : " +artifact.getName() + " in : " +
                 artifact.getFile().getAbsolutePath());
        return artifact.getName();
    }
    @Override
    public void undeploy(Object key) throws CarbonDeploymentException {
        log.info("Undeploying : " + key);
    }
    @Override
    public Object update(Artifact artifact) throws CarbonDeploymentException {
        log.info("Updating : " +artifact.getName() + " in : " +
                 artifact.getFile().getAbsolutePath());
        return artifact.getName();
    }
    @Override
    public URL getLocation() {
        return repository;
    }
    @Override
    public ArtifactType getArtifactType() {
        return artifactType;
    }
}

3. Write an OSGi declarative service component which is used to register the above Deployer instance.

@Component(
        name = "XMLDeployerServiceComponent",
        immediate = true
)
public class XMLDeployerServiceComponent {
    private Log log = LogFactory.getLog(XMLDeployerServiceComponent.class);
    private XMLDeployer xmlDeployer = new XMLDeployer();
    private ServiceRegistration registration;
    @Activate
    public void start(ComponentContext ctxt) {
        registration = ctxt.getBundleContext().
                registerService(Deployer.class.getName(), xmlDeployer, null);
    }
    @Deactivate
    public void stop(ComponentContext ctxt) {
        registration.unregister();
    }
}

4. Once the above are in your project, we need to add the maven scr plugin and bundle plugin section to generate the components level metadata for scr annotations and bundle info. An example section on the pom file will like below

<plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>2.3.5</version>
        <extensions>true</extensions>
        <configuration>
            <instructions>
                <Bundle-Vendor>Sampe Inc</Bundle-Vendor>
                <Bundle-SymbolicName>org.sample.xml</Bundle-SymbolicName>
                <Private-Package>
                    org.sample.xml.deployer.internal
                </Private-Package>
                <Export-Package>
                    !org.sample.xml.deployer.internal,
                    org.sample.xml.deployer.*; version="${project.version}",
                </Export-Package>
                <Import-Package>
                    org.apache.commons.logging.*;version="${commons.logging.import.version.range}",
                    org.osgi.framework.*;version="${osgi.framework.import.version.range}"
                    org.wso2.carbon.deployment.exception.*;version="${carbon.kernel.package.import.version.range}"
                    org.wso2.carbon.deployment.spi.*;version="${carbon.kernel.package.import.version.range}"
                </Import-Package>
            </instructions>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-scr-plugin</artifactId>
        <version>1.9.0</version>
        <executions>
            <execution>
                <id>generate-scr-scrdescriptor</id>
                <goals>
                    <goal>scr</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

5. Build your deployer compoenent, which will generate an OSGi bundle. Now this bundle can be installed into a running carbon server instance using OSGI bundle install commands. By default carbon uses equinox osgi runtime. So once you start the carbon server you will see the commend console of equinox as below.

wso2@work:/media/data/wso2/carbon-kernel/distribution/target/wso2carbon-kernel-5.0.0-SNAPSHOT$ s
JAVA_HOME environment variable is set to /home/wso2/bin/java/jdk1.7.0_40
CARBON_HOME environment variable is set to /media/data/wso2/carbon-kernel/distribution/target/wso2carbon-kernel-5.0.0-SNAPSHOT
osgi>
osgi>

6. Then using the install command you can install the bundle as below. Once you install and start the bundle successfully, you will see the log message of the XMLDeployer instance getting initialized.

osgi> install file:/media/data/wso2/samples/sample-deployer/target/org.sample.xml.deployer-1.0.0-SNAPSHOT.jar
Bundle id is 36
RegisteredServices   null
ServicesInUse        null
LoaderProxy          org.sample.xml; bundle-version="1.0.0.SNAPSHOT"
Fragments            null
ClassLoader          null
Version              1.0.0.SNAPSHOT
LastModified         1390824613943
Headers               Bnd-LastModified = 1390813852361
 Build-Jdk = 1.7.0_40
 Built-By = wso2
 Bundle-Activator = org.sample.xml.deployer.internal.XMLDeployerBundleActivator
 Bundle-ManifestVersion = 2
 Bundle-Name = org.sample.xml.deployer
 Bundle-SymbolicName = org.sample.xml
 Bundle-Vendor = WSO2 Inc
 Bundle-Version = 1.0.0.SNAPSHOT
 Created-By = Apache Maven Bundle Plugin
 Export-Package = org.sample.xml.deployer;uses:="org.wso2.carbon.deployment.spi,org.apache.commons.logging,org.wso2.carbon.deployment,org.wso2.carbon.deployment.exception";version="1.0.0.SNAPSHOT"
 Import-Package = org.apache.commons.logging;version="[1.1,2)",org.osgi.framework;version="[1.7,2)",org.wso2.carbon.deployment;version="[5.0,6)",org.wso2.carbon.deployment.exception;version="[5.0,6)",org.wso2.carbon.deployment.spi;version="[5.0,6)"
 Manifest-Version = 1
 Service-Component = OSGI-INF/org.sample.xml.deployer.XMLDeployer.xml
 Tool = Bnd-1.43.0

7. Once you install and start the bundle successfully, you will see the log message of the XMLDeployer instance getting initialized as below. If this log appears, then the deployer is registered with the deployment framework successfully.

osgi> start 36
[2014-01-27 17:40:39,874]  INFO {org.sample.xml.deployer.XMLDeployer} -  Initializing the XMLDeployer

8. Testing of this deployer can be done by creating a directory named “xmls” under $CARBON_HOME/repository/deployment/server/ and adding a new xml file under this location. Then once the deployment engine scans this directory and find the new artifact, it will call the deploy method of the deployer instance. This will basically print the following log at the console. Similarly, the update and un-deployment of the deployer can also be tested by updating or removing the sample xml file.

[2014-01-27 17:56:15,205]  INFO {org.sample.xml.deployer.XMLDeployer} -  Deploying : sample.xml in : /media/wso2/carbon-kernel/distribution/target/wso2carbon-kernel-5.0.0-SNAPSHOT/repository/deployment/server/xmls/sample.xml
Posted in Carbon5, WSO2 | Tagged , , , , | 1 Comment

[Book] Enterprise Integration with WSO2 ESB

The Packt Publishing recently published a book on “Enterprise Integration with WSO2 ESB”. This was written by Prabath Siriwardana. I was a Technical Reviewer for this book.

Cover

Let me give some high level overview of this book. As a reviewer of this book, I had the opportunity to work on some of the fundamentals of Enterprise Integration Patterns with WSO2 ESB. The patterns supported by WSO2 ESB is explained in their documentation.

This book takes some important patterns, which are widely used at enterprise level and explains them using some well defined examples. Some of the patterns included in this book are :

  • Content-Based Router
  • Dynamic Router
  • Splitter
  • Scatter and Gather
  • Publish and Subscribe
  • Service Chaining
  • Content Enricher
  • Detour

The high level overview of the book includes the following

  • How to implement commonly used Enterprise Integration Patterns with WSO2 ESB
  • How to integrate WSO2 ESB with some well known third party Message Brokers
  • Business messaging and transformation, such as FIX and HL7, with WSO2 ESB
  • Integration with SAP using WSO2 ESB
  • Cloud Connectors (Connect to Twitter) from WSO2 ESB

This book is now available for buying at Packt Publishing.

Posted in Book, WSO2 | Tagged , , , , | Leave a comment