Versions Compared

Key

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

Creating a Hello World JBI

...

Component

This tutorial describes how to create a very simple Hello World style of JBI service engine (SE) component. It demonstrates some best practices for creating JBI components. The example in this component. This tutorial is as minimalistic as possible so as to focus on key concepts and not drown in details. The example Hello World component will respond to all requests with the message:

Panel

Wiki Markup
<hello>Hello World! Message \[<original message here>\] contains \[??\] bytes.</hello>

The following sections will walk through the creation, packaging, testing and deployment of the Hello World component.

Prerequisites

  • Maven 2.0.4 or higher
    • If you have never used Maven previously the Maven Getting Started Guide explains some valuable concepts surrounding Maven
  • ServiceMix 3.1 or higher
  • A broadband internet connection ( so Maven can automatically download dependencies)

A Very Brief Introduction to Java Business Integration

...

JBI components are can be thought of as the smallest applications or services accessible in a service-oriented architecture. Each service has a very specific purpose and therefore a narrow scope and set of functionality. Components come in two flavoursflavors: Service Engine Engines (SE) and Binding Components (BC). Several SUs are packed must be packaged into a SA to be deployed to the JBI container. An SA is a complete application consisting of one or more services interacting with one another. By comparison, this is similar to the way that WAR files must be packaged inside of an EAR file to be deployed to a J2EE container.

See also the page providing information on working with service units

Below are some quick definitions the are dominant throughout the JBI spec:

  • Component Architecture
    • Binding Components - Components that provide or consume services via some sort of communications protocol or other remoting technology
    • Service Engines - Components that supply or consume services locally (within the JBI container)
Note

The difference between binding components (BCs) and service engines (SEs) is definitely subtle and is not denoted by the JBI APIs. In fact, the only real true difference between the two is in the jbi.xml descriptor in the packaging. What it really boils down to is the fact that BCs are used to do integration with a service outside the bus and SEs are services that deployed to and solely contained within the bus. Hopefully the JBI 2.0 spec will provide more distinction.

  • Component Component Packaging
    • Service Units - Packaging for an individual service that allows deployment to the JBI container; similar to a WAR file from J2EE
    • Service Assemblies - Packaging for groups of SUs for deployment to the JBI container; similar to an EAR file from J2EE

For further information on JBIThis tutorial focuses on both component architecture and component packaging. For further information and details on JBI, see the following:

...

Now let's move on to creating the Maven projects for the Hello World component.

...

Creating a Maven

...

Project For the JBI Service Engine

The focus of this section is on the creation of a JBI component. For this task, a Maven archetype will be used to create a Maven project skeleton to house the component. Maven archetypes are templates for Maven projects that jumpstart project creation via the automation of repetitive tasks by following standard conventions. The result of using an archetype to create a Maven project is a directory structure, a Maven POM file and, depending on the archetype being used, sometimes Java objects and JUnit tests.

Below are the steps to follow for creating the directory structure and project. All instructions laid out to take place on a Unix command-line.

1) Create a directory named hello-world-smx and switch to that directory:

Code Block
$ mkdir hello-world-smx
$ cd hello-world-smx

2) Use either the servicemix-service-engine or the servicemix-binding-component Maven archetype to generate a Maven project for the SE:component.

Note

Being that the difference between a BC and a SE is not denoted by the JBI APIs but rather the functionality of the component (see comments above on BCs vs SEs) it is up to you what type of component you create. This tutorial will show the commands for creating both a BC and a SE.

To create a SE, execute the following command on the command-line:

Panel

$ mvn archetype:create \
-DarchetypeGroupId=org.apache.servicemix.tooling \
-DarchetypeArtifactId=servicemix-service-engine \
-DarchetypeVersion=3.1-incubating-SNAPSHOT \
-DgroupId=org.apache.servicemix.samples.helloworld .se \
-DartifactId=hello-world-sesu

To create a BC, execute the following command on the command-line:

Panel

$ mvn archetype:create \
-DarchetypeGroupId=org.apache.servicemix.tooling \
-DarchetypeArtifactId=servicemix-service-engine \
-DarchetypeVersion=3.1-incubating-SNAPSHOT \
-DgroupId=org.apache.servicemix.samples.helloworld \
-DartifactId=hello-world-su

The command above will create a directory named hello-world-su that houses a The command above will create a directory named hello-world-se that houses a Maven project for the JBI service engine being created here. The name of the directory is taken from the artifactId parameter.

The first three parameters to the mvn command (-DarchetypeGroupId=org.apache.servicemix.tooling -DarchetypeArtifactId=servicemix-service-engine -DarchetypeVersion=3.1-incubating-SNAPSHOT) identify which Maven archetype to use for the archetype:create goal, while the last two parameters (-DgroupId=org.apache.servicemix.samples.helloworld -DartifactId=hello-world-sesu) uniquely identify the Maven project that is being generated. The groupId (printed in pink) is used as the Java package and the artifactId is used as the project name. Therefore, only alphanumeric characters are valid values for the groupId and artifactId parameters.

Tip

The value of the archetypeVersion parameter in the command above (3.1-incubating-SNAPSHOT) may need to be updated to the current ServiceMix version in order for the command to work correctly. The latest version can always be found in the top level ServiceMix POM in the <version> element.

The output from executing the archetype:create goal is shown below (only relevant information has been preserved):

\
[INFO
\
] Scanning for projects...
\
[INFO
\
] Searching repository for plugin with prefix: 'archetype'.
\
[INFO
\
] 
\
---------------------------------------------------------------------------
\
-
\
[INFO
\
] Building Maven Default Project
\
[INFO
\
]    task-segment: 
\
[archetype:create
\
] (aggregator-style)
\
[INFO
\
] 
\
---------------------------------------------------------------------------
\
-
[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
\
[INFO
\
] 
<span style="color: #ff00ff">Defaulting package to group ID: org.apache.servicemix.samples.helloWorldSE</span> ... \[INFO\] **\**\**\**\**\**\**\**\**\**\* End of debug info from resources from generated POM **\**\
Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] *************************************************************
\
* 
\
[INFO
\
] 
<span style="color: #009900">Archetype created in dir: /Users/bsnyder/src/hello-world-se/hello-world-se</span> \[INFO\] \-----------------------------------------------------------------------\- \[INFO\] BUILD SUCCESSFUL \[INFO\] \-----------------------------------------------------------------------\-
No Format

Panel
Wiki Markup

Again, Maven creates a directory using the artifactId provided (printed in green in the command and output) as the directory name. Inside this directory resides the pom.xml and the src directory.

Note
titleIn case of a BUILD ERROR: Maven plugin version requirement

The maven-archetype-plugin 1.0-alpha4 or above is required for this tutorial. When an older version is installed, a build error will occur. The version of this plugin can be checked by verifying the name of the following directories:

Code Block
titleUnix

\~/.m2/repository/org/apache/maven/plugins/maven-archetype-plugin 
Code Block
titleWindows

C:\Documents and Settings\<USERNAME>

In case the only version available of the maven-archetype-plugin is an older one, a minimal pom.xml file will need to be created manually in the hello-world-se directory. Below is a simple POM to use for this purpose:

Code Block
titleMinimal pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.servicemix.samples</groupId>
  <artifactId>hello-world-se</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-archetype-plugin</artifactId>
          <version>1.0-alpha-4</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Creating the JBI Service Engine

Let's first examine the classes created by the servicemix-service-engine Maven archetype in this simple SE we're developing. These classes extend class from either the JBI spec APIs or from the servicemix-common package. Now let's look at the classes created by the servicemix-service-engine Maven archetype:

  • MyBootstrap.java - Implements javax.jbi.component.Boostrap which is called by the JBI container 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 convience class that makes creating JBI components much easier and provides 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.
  • MyEndpoint.java - Extends Endpoint and implements ExchangeProcessor. Endpoint provides a referenceable resource for the component and the ExchangeProcessor provides the ability for the JBI container to process a message exchange with the component. The ExchangeProcessor.process() method is where we will make a call to a method containing the logic to print out the Hello World message.

Adding the Logic to Print the Message

When creating a JBI component, how a message exchange is handled depends on whether a component is a consumer or a provider. Because the Hello World SE will not be consuming any other service (i.e., sending a message to another service), it is a provider (i.e., it will only be providing its service via a return message). As mentioned above, the ExchangeProcessor.process() method is of interest because it is where the message exchange is handled, so let's examine this method:

Starting Jakarta Velocity v1.4
[INFO] RuntimeInstance initializing.
[INFO] Default Properties File: org/apache/velocity/runtime/defaults/velocity.properties
[INFO] Default ResourceManager initializing. (class org.apache.velocity.runtime.resource.ResourceManagerImpl)
[INFO] Resource Loader Instantiated: org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader
[INFO] ClasspathResourceLoader : initialization starting.
[INFO] ClasspathResourceLoader : initialization complete.
[INFO] ResourceCache : initialized. (class org.apache.velocity.runtime.resource.ResourceCacheImpl)
[INFO] Default ResourceManager initialization complete.
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Literal
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Macro
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Parse
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Include
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Foreach
[INFO] Created: 20 parsers.
[INFO] Velocimacro : initialization starting.
[INFO] Velocimacro : adding VMs from VM library template : VM_global_library.vm
[ERROR] ResourceManager : unable to find resource 'VM_global_library.vm' in any resource loader.
[INFO] Velocimacro : error using  VM library template VM_global_library.vm : org.apache.velocity.exception.ResourceNotFoundException: 
Unable to find resource 'VM_global_library.vm'
[INFO] Velocimacro :  VM library template macro registration complete.
[INFO] Velocimacro : allowInline = true : VMs can be defined inline in templates
[INFO] Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions
[INFO] Velocimacro : allowInlineLocal = false : VMs defined inline will be  global in scope if allowed.
[INFO] Velocimacro : initialization complete.
[INFO] Velocity successfully started.
[INFO] [archetype:create]
[INFO] Defaulting package to group ID: org.apache.servicemix.samples.helloworld
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating Archetype: servicemix-service-engine:3.1-incubating-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.apache.servicemix.samples.helloworld
[INFO] Parameter: packageName, Value: org.apache.servicemix.samples.helloworld
[INFO] Parameter: basedir, Value: /Users/bsnyder/src/hello-world-smx
[INFO] Parameter: package, Value: org.apache.servicemix.samples.helloworld
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: hello-world-su
[WARNING] org.apache.velocity.runtime.exception.ReferenceException: reference : template = archetype-resources/pom.xml [line 63,column 16] : 
${servicemix-version} is not a valid reference.
[WARNING] org.apache.velocity.runtime.exception.ReferenceException: reference : template = archetype-resources/pom.xml [line 68,column 16] : 
${servicemix-version} is not a valid reference.
[WARNING] org.apache.velocity.runtime.exception.ReferenceException: reference : template = archetype-resources/pom.xml [line 83,column 18] : 
${servicemix-version} is not a valid reference.
[WARNING] org.apache.velocity.runtime.exception.ReferenceException: reference : template = archetype-resources/pom.xml [line 94,column 18] : 
${xbean-version} is not a valid reference.
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] Archetype created in dir: /Users/bsnyder/src/hello-world-smx/hello-world-su
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 seconds
[INFO] Finished at: Fri Jan 05 17:24:39 MST 2007
[INFO] Final Memory: 5M/9M
[INFO] ------------------------------------------------------------------------

Again, Maven creates a directory using the artifactId provided as the directory name. Inside this directory resides the pom.xml and the src directory. If you see the BUILD SUCCESSFUL message, proceed to the next section. Otherwise see the note below about a BUILD ERROR.

Note
titleIn case of a BUILD ERROR: Maven plugin version requirement

The maven-archetype-plugin 1.0-alpha4 or above is required for this tutorial. When an older version is installed, a build error will occur. The version of this plugin can be checked by verifying the name of the following directories:

Code Block
titleUnix

~/.m2/repository/org/apache/maven/plugins/maven-archetype-plugin 
Code Block
titleWindows

C:\Documents and Settings\<USERNAME>

In case the only version available of the maven-archetype-plugin is an older one, a minimal pom.xml file will need to be created manually in the hello-world-su directory. Below is a simple POM to use for this purpose:

Code Block
titleMinimal pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.servicemix.samples.helloworld</groupId>
  <artifactId>hello-world-su</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
Code Block
public void process(MessageExchange exchange) throws Exception { // The component acts as a provider, this means that another component has requested our service // As this exchange is active, this is either an in or a fault (out are send by this component) if (exchange.getRole() == MessageExchange.Role.PROVIDER) { // Check here if the mep is supported by this component if (exchange instanceof InOut == false) { throw new UnsupportedOperationException("Unsupported MEP: " + exchange.getPattern()); } // In message if (exchange.getMessage("in") != null) { NormalizedMessage in = exchange.getMessage("in"); // TODO ... handle the in message // If the MEP is an InOnly, RobustInOnly, you have to set the exchange to DONE status // else, you have to create an Out message and populate it // For now, just echo back NormalizedMessage out = exchange.createMessage(); out.setContent(in.getContent()); exchange.setMessage(out, "out"); channel.send(exchange);

          
// Fault message
<artifactId>maven-archetype-plugin</artifactId>
          
} else if (exchange.getFault() != null) {
<version>1.0-alpha-4</version>
        </plugin>
      
</
/
plugins>
 
TODO
 
...
  </pluginManagement>
  </build>
</project>

Creating the JBI Service Engine

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.
  • MyEndpoint.java - Extends Endpoint and implements ExchangeProcessor. Endpoint provides a referencable resource for the component and the ExchangeProcessor provides the ability for the JBI container to process a message exchange with the component. The ExchangeProcessor.process() method is where we will add custom functionality to print out the Hello World message.
  • 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

When creating a JBI component, how a message exchange is handled depends on whether a component is a consumer or a provider. Because the Hello World component will not be consuming any other service (i.e., sending a message to another service), it is a provider (i.e., it will only be providing its service via a return message). As mentioned above, the ExchangeProcessor.process() method is of interest because it is where the message exchange is handled, so let's examine this method:

Code Block

public void process(MessageExchange exchange) throws Exception {
handle the fault
                exchange.setStatus(ExchangeStatus.DONE);
                channel.send(exchange);
            // This is not compliant with the default MEPs
            } else {
                throw new IllegalStateException("Provider exchange is ACTIVE, but no in or fault is provided");
            }
        // 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
        } else 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
            }// elseThe {
component acts as a provider, this means that another component has requested our service
   // Out message
   // As this exchange is active, this is either an in or a iffault (exchange.getMessage("out") != null) {
are send by this component)
        if (exchange.getRole() == MessageExchange.Role.PROVIDER) {
            // TODOCheck ...here handleif the response
mep is supported by this component
            if (exchange instanceof  exchange.setStatus(ExchangeStatus.DONE);InOut == false) {
               throw new UnsupportedOperationException("Unsupported MEP: " + channelexchange.sendgetPattern(exchange));
            }
    //   Fault message
    // In message
          } else if (exchange.getFaultgetMessage("in") != null) {
                NormalizedMessage in   // TODO ... handle the fault= exchange.getMessage("in");
                // TODO   exchange.setStatus(ExchangeStatus.DONE);
    ... handle the in message
                channel.send(exchange);
                // This is not compliant with the default MEPs
// If the MEP is an InOnly, RobustInOnly, you have to set the exchange to DONE status
                // else, you have to create an Out message }and elsepopulate {it
                // For now,  throw new IllegalStateException("Consumer exchange is ACTIVE, but no out or fault is provided"just echo back
                NormalizedMessage out = exchange.createMessage();
                }out.setContent(in.getContent());
            }
    exchange.setMessage(out, "out");
    // Unknown role
        } else { channel.send(exchange);
            throw// new IllegalStateException("Unkown role: " + exchange.getRole());
Fault message
            }
 else   }

The implementation of this method was provided by the servicemix-service-engine Maven archetype and is shown in the code snippet above. Because the archetype can be used to create a consumer or a provider service engine, this method is very generic and contains a conditional block for handling either a consumer or a provider role. But this method will still require us to make the decision of which style of Message Exchange Pattern (MEP) to handle as well. In the case of the Hello World SE, we know that it is a provider so it will need to send a return message. Therefore it will need to handle an In-Out MEP.

So instead of having MyEndpoint extend the very basic Endpoint class, we're going to extend a different class that is specifically for provider endpoints named
ProviderEndpoint.

Image Removed

Notice the diagram above showing the class hierarchy of Endpoint. The ProviderEndpoint supplies some additional conveniences for provider components and will make the job of implementing MyEndpoint as a provider much easier. So change the definition of MyEndpoint from this:

Code Block

public class MyEndpoint extends Endpoint implements ExchangeProcessor

to this:

Code Block

public class MyEndpoint extends ProviderEndpoint implements ExchangeProcessor

Because the ProviderEndpoint.process() method already handles an In-Out MEP, MyEndpoint will simply need to override the ProviderEndpoint.processInOut() method. Below is the content for implementing this method:

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>."));
}

This method is the logic for the Hello World SE. We're not doing anything complex here at all in order to keep this tutorial very simple. Now it's time to test the component.

Testing the Hello World SE

Thanks to the archetype, testing the component is very easy because it already created a test. The only change we'll make is to the string being sent by the client code. In the src/test/java directory is the org.apache.servicemix.samples.helloworld.se.MySpringComponentTest test. Simply open this test and change line #36 from this:

Code Block

me.getInMessage().setContent(new StringSource("<hello>world</hello>"));

to something more meaningful, like this:

Code Block

me.getInMessage().setContent(new StringSource("<hello>Ski Colorado!</hello>"));

To execute the test, simply run the Maven install goal from within the hello-world-se directory like so:

Code Block

$ mvn install 

Below is the output that will print to the console:

if (exchange.getFault() != null) {
                // TODO ... handle the fault
                exchange.setStatus(ExchangeStatus.DONE);
                channel.send(exchange);
            // This is not compliant with the default MEPs
            } else {
                throw new IllegalStateException("Provider exchange is ACTIVE, but no in or fault is provided");
            }
        // 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
        } else 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);
                    channel.send(exchange);
                // Fault message
                } else if (exchange.getFault() != null) {
                    // TODO ... handle the fault
                    exchange.setStatus(ExchangeStatus.DONE);
                    channel.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 implementation of the method above was provided by the servicemix-service-engine Maven archetype. Because the archetype can be used to create either a consumer or a provider service engine, this method is very generic and contains a conditional block for handling either a consumer or a provider role. But this method will still require us to make the decision of which style of Message Exchange Pattern (MEP) to handle. In the case of the Hello World component, we know that it is a provider so it will need to send a return message. Therefore it will need to handle an In-Out MEP. So instead of having MyEndpoint extend the very basic Endpoint class and implement its own process() method, we're going to extend a different class that is specifically for provider endpoints named
ProviderEndpoint.

Image Added

Notice the diagram above showing the class hierarchy of Endpoint. The ProviderEndpoint supplies some additional conveniences for provider components beyond what the Endpoint class provides and will make the job of implementing MyEndpoint as a provider much easier. So let's make some changes to the MyEndpoint class. First remove the process() method shown above. Second, change the definition of the MyEndpoint class from this:

Code Block

public class MyEndpoint extends Endpoint implements ExchangeProcessor

to this:

Code Block

public class MyEndpoint extends ProviderEndpoint implements ExchangeProcessor

Third, because the ProviderEndpoint.process() method already handles an In-Out MEP, MyEndpoint will simply need to override the ProviderEndpoint.processInOut() method. Below is the content for implementing this method. Copy/paste this method:

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>."));
}

This method is the logic for the Hello World component. We're not doing anything complex here at all in order to keep this tutorial very simple. Now it's time to test the component.

Testing the Hello World Component

Thanks to the archetype, testing the component is very easy because it already created a test. The only change we'll make is to the string being sent by the client code. In the src/test/java directory is the org.apache.servicemix.samples.helloworld.se.MySpringComponentTest test. Simply open this test and change line #36 from this:

Code Block

me.getInMessage().setContent(new StringSource("<hello>world</hello>"));

to something more meaningful, like this:

Code Block

me.getInMessage().setContent(new StringSource("<hello>Ski Colorado!</hello>"));

To execute the test, simply run the Maven install goal from within the hello-world-su directory like so:

Code Block

$ mvn install 

Below is the output that will print to the console:

No Format
Panel
Wiki Markup
\[INFO\] Scanning for projects...
\[INFO\] ----------------------------------------------------------------------------
\[INFO\] Building Hello World Service Engine
\[INFO\]    task-segment: \[install\]
\[INFO\] ----------------------------------------------------------------------------
\[INFO\] \[xbean:mapping \{execution: default\}\]
Checking: org.apache.servicemix.samples.helloworld.se.MyComponent
Checking: org.apache.servicemix.samples.helloworld.se.MyEndpoint
\[INFO\] Generating META-INF properties file: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/META-INF/services/org/apache/xbean/spring/http/org.apache.servicemix.samples.helloworld/1.0 for namespace: http://org.apache.servicemix.samples.helloworld/1.0
\[INFO\] Generating Spring 2.0 handler mapping: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/META-INF/spring.handlers for namespace: http://org.apache.servicemix.samples.helloworld/1.0
\[INFO\] Generating Spring 2.0 schema mapping: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/META-INF/spring.schemas for namespace: http://org.apache.servicemix.samples.helloworld/1.0
\[INFO\] Generating HTML documentation file: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/hello-world-se.xsd.html for namespace: http://org.apache.servicemix.samples.helloworld/1.0
\[INFO\] Generating XSD file: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/hello-world-se.xsd for namespace: http://org.apache.servicemix.samples.helloworld/1.0
\[INFO\] Generating WIKI documentation file: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/hello-world-se.xsd.wiki for namespace: http://org.apache.servicemix.samples.helloworld/1.0
Warning, could not load class: org.apache.servicemix.samples.helloworld.se.MyEndpoint
\[INFO\] ...done.
Downloading: http://repo.mergere.com/maven2/xml-security/xmlsec/1.3.0/xmlsec-1.3.0.pom
\[WARNING\] Unable to get resource from repository central (http://repo1.maven.org/maven2)
Downloading: http://repo.mergere.com/maven2/wss4j/wss4j/1.5.0/wss4j-1.5.0.pom
\[WARNING\] Unable to get resource from repository central (http://repo1.maven.org/maven2)
\[INFO\] \[jbi:generate-jbi-component-descriptor\]
\[INFO\] Generating jbi.xml
\[INFO\] \[resources:resources\]
\[INFO\] Using default encoding to copy filtered resources.
\[INFO\] \[compiler:compile\]
\[INFO\] Nothing to compile - all classes are up to date
\[INFO\] \[resources:testResources\]
\[INFO\] Using default encoding to copy filtered resources.
\[INFO\] \[compiler:testCompile\]
\[INFO\] Nothing to compile - all classes are up to date
\[INFO\] \[surefire:test\]
\[INFO\] Surefire report directory: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/surefire-reports

Wiki Markup
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.apache.servicemix.samples.helloworld.se.MySpringComponentTest
*<hello>Hello World! Message \[<hello>Ski Colorado!</hello>\] contains \[28\] bytes.</hello>*
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.009 sec

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

Wiki Markup\[INFO\] \[jar:jar\] \[INFO\] Building jar: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/hello-world-se-1.0-SNAPSHOT.jar \[INFO\] \[jbi:jbi-component\] \[INFO\] Generating installer /Users/bsnyder/src/hello-world-smx/hello-world-se/target/hello-world-se-1.0-SNAPSHOT-installer.zip \[INFO\] Building jar: /Users/bsnyder/src/hello-world-smx/hello-world-se/target/hello-world-se-1.0-SNAPSHOT-installer.zip \[INFO\] \[install:install\] \[INFO\] Installing /Users/bsnyder/src/hello-world-smx/hello-world-se/target/hello-world-se-1.0-SNAPSHOT.jar to /Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/hello-world-se/1.0-SNAPSHOT/hello-world-se-1.0-SNAPSHOT.jar \[INFO\] Installing /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/hello-world-se.xsd to /Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/hello-world-se/1.0-SNAPSHOT/hello-world-se-1.0-SNAPSHOT.xsd \[INFO\] Installing /Users/bsnyder/src/hello-world-smx/hello-world-se/target/xbean/hello-world-se.xsd.html to /Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/hello-world-se/1.0-SNAPSHOT/hello-world-se-1.0-SNAPSHOT-schema.html \[INFO\] Installing /Users/bsnyder/src/hello-world-smx/hello-world-se/target/hello-world-se-1.0-SNAPSHOT-installer.zip to /Users/bsnyder/.m2/repository/org/apache/servicemix/samples/helloworld/hello-world-se/1.0-SNAPSHOT/hello-world-se-1.0-SNAPSHOT-installer.zip \[INFO\] ------------------------------------------------------------------------ \[INFO\] BUILD SUCCESSFUL \[INFO\] ------------------------------------------------------------------------ \[INFO\] Total time: 15 seconds \[INFO\] Finished at: Thu Jan 04 15:21:00 MST 2007 \[INFO\] Final Memory: 13M/24M \[INFO\] ------------------------------------------------------------------------

Wiki Markup
Notice that not only do we see that the build was successful, but also note the bold text of the message that was printed (*<hello>Hello World! Message \[<hello>Ski Colorado!</hello>\] contains \[28\] bytes.</hello>*). This is the message we were expecting to be output. If you see this, you just wrote a JBI service engine component and tested it successfully.  

...