Chapter 4. Creating a Mule Application

In the previous chapter we explored a case study and learned how to model it in Mule. In this chapter we'll look at how to go about implementing and testing your model. For those of you who have already downloaded and installed Mule, you may want to skip the next section and go directly to "The Mule IDE."

Core Tools and Components

To start with you'll need to have the following, at minimum:

  • A Java platform: Anything that runs JDK 1.5 or higher available from http://java.sun.com/javaee/downloads/index.jsp.

  • A Mule distribution: Get the latest one from http://mule.mulesource.org/display/MULE/Download.

  • An IDE: Eclipse or IntelliJ are two popular choices.

Note

All references to an IDE in this book refer to the Eclipse IDE.

Installing Mule

First you should download the Mule distribution and unpack it to your hard disk. Next read INSTALL.txt in the root of the Mule distribution and visit http://www.mulesource.org/display/MULE2INTRO/Installing+Mule to get your environment set up correctly. Of course if you don't have a Java runtime or SDK installed you'll need to do that from the Sun site. In addition to this it is important to define an environment variable, MULE_HOME, which specifies the path to the Mule distribution.

Finally, for application development you'll need to set up an IDE. We refer to Eclipse in this book but you can work with the environment of your choice.

The Mule IDE

An IDE project for Mule (separate from the core Mule project) is available as an Eclipse plug-in from the MuleForge site. At the time of this writing, a developer milestone was available for it but was not yet complete. The Mule IDE is designed for Eclipse Europa and depends on the Eclipse Modeling Framework (EMF) and the Graphical Editing Framework (GEF), both of which are available from the main Eclipse site.

Tip

You can find the Mule IDE on the MuleForge site at http://www.mulesource.org/display/MULEIDE/Home.

The intention is to have a fully visual, drag-and-drop interface that will allow you to build Mule applications without needing to delve into the configuration itself. At the moment this feature is not yet available. Currently you can use the IDE to view your services graphically on a "services" page (see Figure 4-1), which appears as a separate page within any Mule XML configuration file and is accessed from the Services tab. This page lets you view the services one by one and inspect the properties of the various elements that make up a service. You can modify these properties by double-clicking elements or right-clicking and selecting "Show Properties View". A "properties" page then displays below the services view listing properties that may be edited.

You can also use the IDE to view global settings for the Mule project on an "overview" page that lets you define which namespaces to use, what global endpoints you may have, a list of connectors, a list of transformers, and a list of globally-available filters.

The Mule IDE services view

Figure 4.1. The Mule IDE services view

By the time you read this, additional features may be available. Until the IDE reaches GA (General Availability), we recommend you evaluate it, but do not expect to be able to make it part of your day-to-day toolkit for Mule development.

Creating Your Mule Application

Once you have your tools and components in place and configured, creating and running a Mule application is quite straightforward.

Service Components

For starters you will need to put the POJOs (Plain Old Java Objects) and/or beans that you intend to use in your application in place. This might be as simple as copying them over from some other project or location, or you might need to code some or all of them from scratch. The great thing is that Mule does not require you to learn an API to implement services. If you have existing POJOs and beans you can simply drop them in without modification. If you need to create them you simply focus on the function they are to perform.

This is because Mule uses an entry point resolver that passes the payload of a message to your service component, looking for a method that accepts an object of the same type as the payload (to learn more about Mule Messages see "Messages in Mule" later in this chapter). If it doesn't find such a method or finds more than one, it will throw an exception. There are ways to override this behavior: you can implement the org.mule.api.lifecycle Callable interface in your service component or use a different entry point resolver—one provided with Mule or one that you build yourself.

There is also a powerful API you can use when developing your service components. Using it gives you greater control over your component and full access to the Mule server hosting it; however, it also couples your components with Mule. Best practice dictates that you should keep your service components as loosely coupled as possible. We will explore some aspects of the API when we discuss unit testing in this chapter, and we will cover creating your own Mule components, such as routers and transformers, in Chapter 7.

Tip

You can learn more about creating service components at http://mule.mulesource.org/display/MULE2USER/Developing+Service+Components.

The Mule Configuration File

Once you have developed or copied the POJOs and beans that you will be using, you need to build your Mule configuration file and define how you want to wire your components together, specifying the routing, transports, and transformers to be used. We looked at how to create a Mule configuration in Chapter 1.

After you create your config xml file, ensure that it is located in the correct location—namely a folder that is in the classpath at the time Mule is launched. If this is not the case you will have to provide a fully-qualified path for the file when launching Mule.

The default name for this file is mule-config.xml but you can use any name for it. You can also split the file up into multiple files and include them as a comma-separated list, for example on the command line (see the next section, "Running Your Mule Application").

Running Your Mule Application

The simplest way to run Mule is to launch it from the command prompt, as this example shows:

"%MULE_HOME%inmule.bat" -config bin/mule-config.xml

This will look for the Mule launch script in the bin folder of MULE_HOME(see the previous "Installing Mule" section) and will run Mule by loading the configuration file mule-config.xml from the bin folder located in the current folder.

The -config parameter allows you to specify a fully-qualified or relative path and file name for the configuration file to use as shown in the preceding code snippet. Assuming that you have the JDK installed on your computer and that it is configured properly, the single command listed previously will work from within a script. This will deploy the wrapper that will, in turn, launch Mule. This is the most common method used to deploy Mule as a stand-alone application.

If you have a single configuration file named mule-config.xml that is located in the folder from which you launch Mule, or is in the classpath, all you need to do is type mule at the command prompt This will launch Mule as a foreground process.

Mule can be launched programmatically by using the org.mule.MuleServer class. This class needs to be created and its constructor must accept a comma-separated list of configuration files that you want to use, as shown here:

MuleServer server = new MuleServer ("myConfig.xml,
    myOtherConfig.xml");
server.start (true);

Installing Mule as a Service (or Daemon)

Apart from being run in a stand-alone fashion, Mule can also be installed as a service on Windows or as a daemon on Unix.

On Windows use the install parameter shown here:

mule install -config myConfig.xml

This can be reversed by using the following remove command:

mule remove

Once installed, Mule can be started, stopped, or restarted. In each case the config parameter will accept a comma-separated list of configuration files to use:

mule start | restart | stop -config myConfig.xml

Additionally, you can use the following Windows net command to start and stop Mule:

net start | stop mule

On *nix you can use the service command to start, stop, and restart Mule, but this is only applicable if your OS supports SysV-style startup systems.

Tip

You can also deploy Mule by embedding it in a Java application, web application, application server, or Spring application. You can learn more about these options at http://www.mulesource.org/display/MULE2USER/Deployment+Scenarios.

To browse the Mule wiki for more information on running Mule, go to http://www.mulesource.org/display/MULE2INTRO/Running+Mule.

Unit Tests

Having a modeled application is not much help unless you can verify that the model functions the way it is supposed to. In this section we will investigate how you can test a Mule application, after which you will be able to implement such a test yourself.

Testing Applications

The simplest way to test an application—however large or small—is to repeatedly run the program, feed it various values, and ensure that the outputs are what they should be. Of course, in such a case, it is also simple to draw up a chart of which inputs should produce a particular series of outputs. Mule is no different, and while it can be launched from the command line, the rather lengthy log files can be particularly daunting to read, parse, and filter; especially if you're trying to debug a Mule application. The amount and type of entries in the log file can be adjusted by tweaking the logger settings, but this means that some useful information might be excluded, which can be counter productive.

An automatic series of tests would be the ideal solution since this will consistently run all the tests necessary to prove your application. The xUnit family of tests is the most commonly-used test framework. Pioneered in the 1990s by Kent Beck and Erich Gamma, the concept has been ported to numerous languages, including Java. In fact, the popularity of the Java language means that the JUnit family of tests is perhaps the most commonly used.

JUnit allows you to implement several testing paradigms. Black box testing involves testing the results of an application without knowing what the program's internals looks like. White box testing is the opposite, where the tests are intimately aware of how the program works. Grey box testing is a combination of both.

When testing a Mule application, we normally use black box testing.

The main advantage of a unit test is that it allows you to repetitively test the code using the same parameters. While developing, you can use the tests to see how successful your code is. These tests, if complete, can also serve as documentation for your classes—the more tests you have, the easier it is to see what the original application is meant to do.

Of course, such tests help make bug locating and fixing a lot easier since you only need to inspect the tests that fail to figure out where problems arise. It is good practice to always add new test cases with different scenarios and possibilities. This is useful when you need to refactor code at a later stage, or when you need to modify the original application or classes.

Black Box Testing a Mule Server

Figure 4-2 illustrates the communication between your test case and a Mule server. Test data is sent to the server in the form of data on one or more endpoints. These endpoints are read and the data processed according to Mule's configuration. The test case can then monitor the endpoints from which it is expecting results without knowing how the output is being generated.

Black box testing

Figure 4.2. Black box testing

Black box testing involves being able to send information to an application and examine the output. The test will not contain any assumptions or details about the program itself; there is no way to tell, just by examining the test, how an input A causes an output B.

Mule applications can easily be tested in this manner since you can place information on a valid endpoint and, if your Mule server is properly configured, you will receive a result on another endpoint. You can compare this output with your expected results to make sure that your Mule server is properly configured.

Creating a Test Case

JUnit test cases are normally simple Java classes that can interact with the items that are under test. This is not enough for a Mule application since you need to make sure that a Mule server is loaded and available before it can be used. Mule has a Java class, org.mule.tck.FunctionalTestCase, that can be used precisely for this sort of thing; this class can be found inside the mule-tests-functional.jar.

The FunctionalTestCase class takes care of all necessary Mule initialization and start-up processes and lets you focus on your test. All JUnit tests that you want to write for Mule applications should extend FunctionalTestCase to avoid any unnecessary coding. The only variables that FunctionalTestCase needs are the location and name of the Mule configuration file. This can be achieved through a single method that has to be overridden—getConfigResources()—which is meant to return the full or relative path to the configuration file.

FunctionalTestCase will reinitialize the Mule server for every test that you create. This means that every test will cause Mule to be reloaded and connections to all external items, like JMS servers and databases, will be reconnected. This may be desirable in some cases but can also incur an overhead. This behavior can be changed by configuring FunctionalTestCase to reuse the same Mule instance. This is done by setting the DisposeManagerPerSuite flag to true.

The code in the following example shows an empty test suite that extends the FunctionalTestCase class and implements the getConfigResources() method. The relative path to the configuration file is relative to the Mule server's current directory.

package com.ricston.mule.training.examples;

import org.mule.tck.FunctionalTestCase;

public class testApplication extends FunctionalTestCase
{
   public void testFirstCondition() throws Exception{
     // TODO: Fill in code ...
}

  protected String getConfigResources()
  {
     return "../conf/mule-config.xml";
  }
}

Before we can look at the integration test, we need to explain what a message means to Mule and what APIs there are to manipulate and control a Mule message.

Messages in Mule

Communication with a Mule server can be performed by placing a message on an endpoint. The endpoint can be any valid endpoint but what should the message look like?

Figure 4-3 shows all the different parts of a message in Mule.

The Mule message

Figure 4.3. The Mule message

The MuleMessage Interface

Actual data received on an endpoint is packaged into an object that implements the MuleMessage interface. This object will contain the following:

  • A series of properties that will vary depending on the transport in use.

  • The entire data as the payload of the MuleMessage. This can be any sort of object.

  • If applicable, a series of attachments that accompany the message.

Messages within a Mule instance are routed and handled as MuleMessage objects; however, it is the payload that is passed to the method that is to be executed. The same applies to the return value of a component; it is wrapped up as a payload of a MuleMessage. This layer of abstraction allows Mule elements such as routers and transformers to work with any data type independently of the communication protocols used.

Apart from the payload, a MuleMessage has a number of properties that contain metadata about the payload. These name-value pairs are heavily dependent on the transport used, for example; a JMS message will have a JMSPriority property that an SMTP message will not. The SMTP message, on the other hand, will have a From property.

All this conversion and encapsulation is totally transparent to the Mule developer since Mule handles the necessary conversions on demand. You will also need to create or manipulate MuleMessage objects if you need to send or receive messages programmatically, for example within unit tests.

MuleMessage Methods

A MuleMessage exposes the following methods, which allow you to inspect and manipulate the payload and the properties:

  • getPayload() will retrieve the payload inside the MuleMessage. This will be the transformed payload.

  • getPayload (Class) will retrieve the payload inside the MuleMessage and attempt to transform it to the specified class. Mule does this by using an internal registry of transformers that are available to best transform the payload into the required type.

  • getPayloadAsBytes() will retrieve the payload inside the MuleMessage and convert it to a byte array.

  • getPayloadAsString() will retrieve the payload inside the MuleMessage and convert it to a string.

  • getOriginalPayload() will retrieve the untransformed payload.

  • setPayload(Object) will override the current payload with the new object.

  • getProperty (String) will return the value of a property as an object for a given name. If you know the data type of the property you can use specialized methods such as getBooleanProperty(), getIntProperty(), and so forth.

  • setProperty (String, Object) will set the value of a property for a given name. If you know the data type of the property you can use specialized methods such as setBooleanProperty and setIntProperty rather than passing a generic object.

  • getPropertyNames() will retrieve a map that contains all the names of all the properties.

  • getAttachment (String) will retrieve a specific attachment. A MuleMessage can have multiple attachments.

  • getAttachmentNames() will return a map containing a list of the names of all attachments.

  • addAttachment(String, DataHandler) will add an attachment to the MuleMessage.

Tip

The JavaDocs contain a more complete description of this and all other Mule classes. They can be found on the MuleSource wiki at http://www.mulesource.org.

Implementing MuleMessage

There is one implementation of MuleMessage available in the org.mule package, called DefaultMuleMessage. This is nothing more than a wrapper object that contains the properties and payload of a message.

In code you can create MuleMessage or DefaultMuleMessage objects by constructing a new DefaultMuleMessage. Its constructor is overloaded to accept data that will be used as the payload. In the example that follows, the first line of code constructs a new variable called aMsg that will be a new DefaultMuleMessage object with a string payload. In the second example, the payload is initially empty:

MuleMessage aMsg = new DefaultMuleMessage("bc");
DefaultMuleMessage Msg = new DefaultMuleMessage();

Communicating with the Mule Server

One way of communicating with the Mule server is to use the MuleClient class. This class is available in the org.mule.module.client.MuleClient package and allows you to send and receive events to and from a Mule server. The following methods are available:

  • dispatch(String, MuleMessage) will send a MuleMessage to the Mule server along a given endpoint asynchronously.

  • send(String, MuleMessage,int) will send a MuleMessage to the Mule server along a given endpoint synchronously.

  • sendAsync(String, MuleMessage) will send a synchronous MuleMessage to the Mule server without blocking, that is it will simulate asynchronicity.

  • request(String,Long) will receive a message from the Mule server from the specified endpoint.

Asserting Output

You can verify a Mule server's output by comparing it with the expected result. JUnit provides you with a series of assertion statements that let you make these comparisons and that will raise exceptions if the assertion is not true. Your test case will therefore fail with the appropriate error message, clearly identifying which assertion has failed. The most common assertion statements are as follows:

  • assertNull() checks that the object passed to it is a null object.

  • assertNotNull() checks that the object passed to it is not null.

  • assertEquals() checks that the two items passed to it are equal to one another. This method is overloaded to accept different data types.

  • assertTrue() checks that the boolean condition passed to it evaluates to true.

  • assertFalse() checks that the boolean condition passed to it evaluates to false.

  • assertSame() checks that the two objects passed to it refer to the same object.

  • assertNotSame() checks that the two objects passed to it do not refer to the same object.

Testing Mule Using JUnit

As an example, let's take a couple of simple Java objects—one that contains a single method that squares any integer it receives, and another that will inverse any integer it receives.

These two components will be hosted within a Mule server such that an integer passed along vm://math will be received by the first service. The result will be sent to the second class on vm://inverse and the result of this passed back along vm://math.

This is demonstrated in Figure 4-4. Note that since there is no outbound route defined for the inverse service, the result will be sent back along vm://inverse and then along vm://math. This will automatically be a synchronous process. Inside a test case we can send and receive along vm://math.

JUnit example

Figure 4.4. JUnit example

These are the Mule services for the example:

<service name="Square">
   <inbound>
      <vm:inbound-endpoint path="math"/>
   </inbound>
   <component class="com.ricston.tests.Square"/>
   <outbound>
      <outbound-pass-through-router>
         <vm:outbound-endpoint path="inverse"/>
      </outbound-pass-through-router>
   </outbound>
</service>

<service name="Inverse">
   <inbound>
      <vm:inbound-endpoint path="inverse" />
   </inbound>
   <component class="com.ricston.tests.Inverse"/>
</service>

Note

Here we are not going to perform unit tests on the Square and Inverse POJOs—those would be done separately. The focus of the next test is to verify the Mule application—to check that the endpoints, routers, transformers, filters, and components together function as expected. This is commonly referred to as integration testing.

Next we have this source code for the test case:

public class testMuleMath extends
FunctionalTestCase {

public void testWithFive() throws Exception{
   MuleClient client = new MuleClient();
   MuleMessage reply = client.send
      ("vm://math", Integer (5));

      assertNotNull(reply);
      assertNotNull(reply.getPayload());
      Integer result = (Integer)reply.getPayload();
      assertEquals(result.intValue(),-25);
    }
}

There is only one test here—using the number 5 as an input, we're expecting the result to be −25. The lines of code with line numbers are explained in greater detail here:

  • Construct a new MuleClient to be able to communicate with the Mule server.

  • Send the number 5 to the endpoint called vm://math and wait for a reply. The reply will be saved into a new MuleMessage object.

  • Assert that the reply is not null.

  • Assert that the payload of the message is not null.

  • Retrieve the payload and typecast it as an integer.

  • Assert that this integer matches the expected result.

Note that this kind of test is a black box test—the test has no idea how the number 5 became the number −25; it merely confirms that it does. While we know that two POJOs were being used, if this is changed to include any number of POJOs, the test will still send and receive along vm://math and expect the result to be −25.

Tip

If you use global endpoints your XML config and test cases could reference endpoints by their logical name rather than physical representation, thereby reducing maintenance.

Summary

Modeling applications in Mule involves thinking about the different steps that need to be taken for individual services that Mule will host or interact with. Routers will govern how the data will flow from one service to another and transformers will be needed to convert data from one format to another.

The Mule application can be built from such a model by using VM channels initially to let you focus on the routing logic; you can replace these endpoints with the live transports once you are satisfied that the routing has been implemented properly.

As you build your Mule application you'll need to test and validate your model using JUnit and Mule's FunctionalTestCase class. Once you start creating tests, the MuleClient class lets you interact with a Mule server by sending messages (typically DefaultMuleMessage objects that implement the MuleMessage interface). Your tests can be confirmed using standard JUnit assertions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset