Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This command should produce the following output:

No Format

Again, the key here is to make sure you see BUILD SUCCESSFUL. This means that the project skeleton created by the archetype was compiled, packaged and tested successfully. Now we just need to add some custom functionality.

Creating the JBI Component

Before we create any custom functionality, let's first examine some of the items generated by the servicemix-service-engine Maven archetype in this simple component we're developing. These classes extend class from either the JBI spec APIs or from the servicemix-common package.

  • pom.xml - This is the Maven POM] file. This XML file contains all the metadata related to the project so Maven can carry out its functionality.
  • MyBootstrap.java - Implements javax.jbi.component.Boostrap which is called by the JBI container as part of the component lifecycle (i.e.g, when the component is installed and uninstalled). This is where you place logic to set up and tear down things when the component is started and stopped.
  • MyComponent.java - Extends the DefaultComponent, a convenience class that makes creating JBI components much easier and provides some additional lifecycle management of components deployed to the JBI container. This class should be fleshed out by overriding methods in the DefaultComponent to configure and initialize the component.
  • MyConsumerEndpoint.java - Extends ConsumerEndpoint and implements MyEndpointType. If you'd like to create a BC that fulfills the consumer role, implement the process() method in this class.
  • MyEndpointType.java - This class is simply an interface marker for Apache XBean so it can generate an XML schema document.
  • MyProviderEndpoint.java - Extends ProviderEndpoint and implements MyEndpointType. If you'd like to create a BC that fulfills the provider role, depending on the MEP being supported, implement the processInOnly() method or the processInOut() method in this class.
  • MySpringComponentTest.java - A simple JUnit test class that extends a helper class to make configuring ServiceMix very easy.
  • src/test/resources/spring.xml - The very simple and generic ServiceMix configuration file.

Now that we've gotten a bird's eye view of what we're working with, let's proceed to adding the custom functionality.

Adding Custom Functionality

Before creating custom functionality for the BC, you need to understand the role of a JBI BC. A BC is simply a binding to a service that is external to the JBI normalized message router (NMR) using some type of communications protocol (e.g., FTP, HTTP, JMS, etc.). It's also the responsibility of the BC to handle any conversion of the message format into Normalized Messages that can be sent along to the NMR. This is known as message normalization.

For example, if we were to create a BC that uses SNMP as the application layer protocol, the SNMP RFC specifies the message format to be used with particular versions of SNMP. It would be the responsibility of the BC to handle not only the communication via the SNMP protocol but also to handling the marshalling of SNMP messages to/from JBI normalized messages. The BC would simply be a binding to a service external to the NMR that speaks SNMP messages via the SNMP protocol.

More on this later in the tutorial. For now, let's proceed with the custom functionality.

Note
titleUsing an IDE

It is at this stage that you should employ the use of an IDE. An IDE can dramatically reduce the work necessary to import clases, override methods and so much more. Because Maven can generate project files for Eclipse and IntelliJ IDEA, either one can be used. Throughout this tutorial, Eclipse will be used. To generate project files for Eclipse, execute the Maven eclipse:eclipse goal and then import the project into your Eclipse IDE.

The creation of a binding component is dependent upon the role that it will play. BCs are consumers, providers or both. Below are definitions of the two roles as they pertain to BCs:

  • Consumer - A consumer BC receives requests from an external service and publishes those requests to the NMR.
  • Provider - A provider BC receives requests from the NMR and publishes those requests to an external service.

This is why both the MyConsumerEndpoint.java and the MyProviderEndpoint.java exist when using the servicemix-binding-component archetype to create a Maven project for a SU. This way the BC that you're creating can play both the consumer role and the provider role. For the sake of this tutorial, we will create implement both roles.

First let's implement the consumer functionality. To do so, open MyConsumerEndpoint.java and let's take a look at he process() method as shown below:

Code Block

public void process(MessageExchange exchange) throws Exception {
    // TODO: As we act as a consumer (we just send JBI exchanges)
    // we will receive responses or DONE / ERROR status here
}

One important item of note before we get started is that this tutorial will not be accessing any services external to the JBI container. The reason for this is that setting up a service external to the JBI container would dramatically increase the complexity of this tutorial. Instead, we will just simulate such functionality by hard-coding some text to be returned.

This method is just a stub that needs to be filled in with our custom functionality. Take note of the comment in that method stub stating that this method will send JBI message exchanges and will receive responses or status messages in this method. Based on these comments, we know that we have a few tasks to handle in the implementation of this method. So let's get started.

Below is the method body that can be copied and pasted into the method stub to being adding some custom functionality. Following the display of this method, we will pick apart this method a bit to explain the various pieces of logic:

Code Block
titleThe MyConsumerEndpoint.process() Method

public void process(MessageExchange exchange) throws Exception {
    // TODO: As we act as a consumer (we just send JBI exchanges)
    // we will receive responses or DONE / ERROR status here

    // The component acts as a consumer, this means this exchange is received because
    // we sent it to another component.  As it is active, this is either an out or a fault
    // If this component does not create / send exchanges, you may just throw an UnsupportedOperationException
    if (exchange.getRole() == MessageExchange.Role.CONSUMER) {
        // Exchange is finished
        if (exchange.getStatus() == ExchangeStatus.DONE) {
            return;
        // Exchange has been aborted with an exception
        } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
            return;
        // Exchange is active
        } else {
            // Out message
            if (exchange.getMessage("out") != null) {
                // TODO ... handle the response
                exchange.setStatus(ExchangeStatus.DONE);
                getChannel().send(exchange);
            // Fault message
            } else if (exchange.getFault() != null) {
                // TODO ... handle the fault
                exchange.setStatus(ExchangeStatus.DONE);
                getChannel().send(exchange);
            // This is not compliant with the default MEPs
            } else {
                throw new IllegalStateException("Consumer exchange is ACTIVE, but no out or fault is provided");
            }
        }
    // Unknown role
    } else {
        throw new IllegalStateException("Unkown role: " + exchange.getRole());
    }
}

The method above takes into account a number of conditions, so let's go through it all. The first thing to note is that this method will only handle the consumer role. This can be seen the outermost condition as displayed below:

Code Block

if (exchange.getRole() == MessageExchange.Role.CONSUMER) {
...
} else {
    throw new IllegalStateException("Unkown role: " + exchange.getRole());
}

If a request comes through for the provider role, an exception will be thrown. Next, the status of the message exchange is checked to make sure it is not an error or a completed situation:

Code Block

if (exchange.getStatus() == ExchangeStatus.DONE) {
    return;
// Exchange has been aborted with an exception
} else if (exchange.getStatus() == ExchangeStatus.ERROR) {
    return;

The exchange status comes directly from the JBI spec and can one of either ACTIVE, DONE or ERROR. As long a the status is ACTIVE the method proceeds on with the custom functionality. This is shown below but we have not implemented any custom logic yet:

...


[INFO] Scanning for projects...
[INFO] ----------------------------------------------------------------------------
[INFO] Building A custom project
[INFO]    task-segment: [install]
[INFO] ----------------------------------------------------------------------------
[INFO] [xbean:mapping {execution: default}]
Checking: org.apache.servicemix.samples.helloworld.bc.MyComponent
Checking: org.apache.servicemix.samples.helloworld.bc.MyConsumerEndpoint
Checking: org.apache.servicemix.samples.helloworld.bc.MyProviderEndpoint
[INFO] Generating META-INF properties file: /private/tmp/hello-world-bc-su/target/xbean/META-INF/services/org/apache/xbean/spring/http/org.apache.servicemix.samples.helloworld.bc/1.0 for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
[INFO] Generating Spring 2.0 handler mapping: /private/tmp/hello-world-bc-su/target/xbean/META-INF/spring.handlers 
for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
[INFO] Generating Spring 2.0 schema mapping: /private/tmp/hello-world-bc-su/target/xbean/META-INF/spring.schemas 
for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
[INFO] Generating HTML documentation file: /private/tmp/hello-world-bc-su/target/xbean/hello-world-bc-su.xsd.html 
for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
[INFO] Generating XSD file: /private/tmp/hello-world-bc-su/target/xbean/hello-world-bc-su.xsd 
for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
[INFO] Generating WIKI documentation file: /private/tmp/hello-world-bc-su/target/xbean/hello-world-bc-su.xsd.wiki 
for namespace: http://org.apache.servicemix.samples.helloworld.bc/1.0
Warning, could not load class: org.apache.servicemix.samples.helloworld.bc.MyEndpointType: java.lang.ClassNotFoundException: 
org.apache.servicemix.samples.helloworld.bc.MyEndpointType
[INFO] ...done.
[INFO] [jbi:generate-jbi-component-descriptor]
[INFO] Generating jbi.xml
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
Compiling 5 source files to /private/tmp/hello-world-bc-su/target/classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
Compiling 1 source file to /private/tmp/hello-world-bc-su/target/test-classes
[INFO] [surefire:test]
[INFO] Surefire report directory: /private/tmp/hello-world-bc-su/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.apache.servicemix.samples.helloworld.bc.MySpringComponentTest
log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory).
log4j:WARN Please initialize the log4j system properly.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.788 sec

Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] [jar:jar]
[INFO] Building jar: /private/tmp/hello-world-bc-su/target/hello-world-bc-su-1.0-SNAPSHOT.jar
[INFO] [jbi:jbi-component]
[INFO] Generating installer /private/tmp/hello-world-bc-su/target/hello-world-bc-su-1.0-SNAPSHOT-installer.zip
[INFO] Building jar: /private/tmp/hello-world-bc-su/target/hello-world-bc-su-1.0-SNAPSHOT-installer.zip
[INFO] [install:install]
[INFO] Installing /private/tmp/hello-world-bc-su/target/hello-world-bc-su-1.0-SNAPSHOT.jar to 
/Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/bc/hello-world-bc-su/1.0-SNAPSHOT/hello-world-bc-su-1.0-SNAPSHOT.jar
[INFO] Installing /private/tmp/hello-world-bc-su/target/xbean/hello-world-bc-su.xsd to 
/Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/bc/hello-world-bc-su/1.0-SNAPSHOT/hello-world-bc-su-1.0-SNAPSHOT.xsd
[INFO] Installing /private/tmp/hello-world-bc-su/target/xbean/hello-world-bc-su.xsd.html to 
/Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/bc/hello-world-bc-su/1.0-SNAPSHOT/hello-world-bc-su-1.0-SNAPSHOT-schema.html
[INFO] Installing /private/tmp/hello-world-bc-su/target/hello-world-bc-su-1.0-SNAPSHOT-installer.zip to 
/Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/bc/hello-world-bc-su/1.0-SNAPSHOT/hello-world-bc-su-1.0-SNAPSHOT-installer.zip
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16 seconds
[INFO] Finished at: Tue Feb 06 15:12:48 MST 2007
[INFO] Final Memory: 15M/32M
[INFO] ------------------------------------------------------------------------

Again, the key here is to make sure you see BUILD SUCCESSFUL. This means that the project skeleton created by the archetype was compiled, packaged and tested successfully. Now we just need to add some custom functionality.

Creating the JBI Component

Before we create any custom functionality, let's first examine some of the items generated by the servicemix-service-engine Maven archetype in this simple component we're developing. These classes extend class from either the JBI spec APIs or from the servicemix-common package.

  • pom.xml - This is the Maven POM] file. This XML file contains all the metadata related to the project so Maven can carry out its functionality.
  • MyBootstrap.java - Implements javax.jbi.component.Boostrap which is called by the JBI container as part of the component lifecycle (i.e.g, when the component is installed and uninstalled). This is where you place logic to set up and tear down things when the component is started and stopped.
  • MyComponent.java - Extends the DefaultComponent, a convenience class that makes creating JBI components much easier and provides some additional lifecycle management of components deployed to the JBI container. This class should be fleshed out by overriding methods in the DefaultComponent to configure and initialize the component.
  • MyConsumerEndpoint.java - Extends ConsumerEndpoint and implements MyEndpointType. If you'd like to create a BC that fulfills the consumer role, implement the process() method in this class.
  • MyEndpointType.java - This class is simply an interface marker for Apache XBean so it can generate an XML schema document.
  • MyProviderEndpoint.java - Extends ProviderEndpoint and implements MyEndpointType. If you'd like to create a BC that fulfills the provider role, depending on the MEP being supported, implement the processInOnly() method or the processInOut() method in this class.
  • MySpringComponentTest.java - A simple JUnit test class that extends a helper class to make configuring ServiceMix very easy.
  • src/test/resources/spring.xml - The very simple and generic ServiceMix configuration file.

Now that we've gotten a bird's eye view of what we're working with, let's proceed to adding the custom functionality.

Adding Custom Functionality

Before creating custom functionality for the BC, you need to understand the role of a JBI BC. A BC is simply a binding to a service that is external to the JBI normalized message router (NMR) using some type of communications protocol (e.g., FTP, HTTP, JMS, etc.). It's also the responsibility of the BC to handle any conversion of the message format into Normalized Messages that can be sent along to the NMR. This is known as message normalization.

For example, if we were to create a BC that uses SNMP as the application layer protocol, the SNMP RFC specifies the message format to be used with particular versions of SNMP. It would be the responsibility of the BC to handle not only the communication via the SNMP protocol but also to handling the marshalling of SNMP messages to/from JBI normalized messages. The BC would simply be a binding to a service external to the NMR that speaks SNMP messages via the SNMP protocol.

More on this later in the tutorial. For now, let's proceed with the custom functionality.

Note
titleUsing an IDE

It is at this stage that you should employ the use of an IDE. An IDE can dramatically reduce the work necessary to import clases, override methods and so much more. Because Maven can generate project files for Eclipse and IntelliJ IDEA, either one can be used. Throughout this tutorial, Eclipse will be used. To generate project files for Eclipse, execute the Maven eclipse:eclipse goal and then import the project into your Eclipse IDE.

The creation of a binding component is dependent upon the role that it will play. BCs are consumers, providers or both. Below are definitions of the two roles as they pertain to BCs:

  • Consumer - A consumer BC receives requests from a service external to the JBI container and publishes those requests to the NMR.
  • Provider - A provider BC receives requests from the NMR and publishes those requests to a service external that is external to the JBI container.

This is why both the MyConsumerEndpoint.java and the MyProviderEndpoint.java exist when using the servicemix-binding-component archetype to create a Maven project for a SU. This way the BC that you're creating can play either the consumer role or the provider role or both. For the sake of this tutorial, we will implement the provider role. Let's proceed to implement the provider functionality. To do so, open MyProviderEndpoint.java and let's take a look at he processInOut() method as shown below:

Code Block

protected void processInOut(MessageExchange exchange, NormalizedMessage in, NormalizedMessage out) throws Exception {
    throw new UnsupportedOperationException("Unsupported MEP: " + exchange.getPattern());
}

One important item of note before we get started is that this tutorial will not actually be accessing any services external to the JBI container. The reason for this is that setting up a service external to the JBI container would dramatically increase the complexity of this tutorial. Instead, we will just simulate such functionality by hard-coding some text to be returned.

The processInOut() method is just a stub that needs to be filled in with our custom functionality. Below is the method body that can be copied and pasted into the method stub to being adding some custom functionality. Following the display of this method, we will pick apart this method a bit to explain the various pieces of logic:

Code Block

protected void processInOut(MessageExchange exchange, NormalizedMessage in, NormalizedMessage out) throws Exception {
	SourceTransformer sourceTransformer = new SourceTransformer();
	String inMessage = sourceTransformer.toString(in.getContent()); 
	out.setContent(new StringSource("<hello>Hello World! Message [" + inMessage + "] contains [" + inMessage.getBytes().length + "] bytes</hello>."));
}

Adding this method will require the import of the following classes:

  • org.apache.servicemix.jbi.jaxp.SourceTransformer
  • org.apache.servicemix.jbi.jaxp.StringSource

These classes can be found in the servicemix-core project.

Note
titleImportant Information!

This tutorial will not be accessing any services external to the JBI container. Instead, we will just simulate such functionality by hard-coding some text to be returned.

...