Getting started with Mule

I intend to write a few blogs about Mule, probably the most-used open source Enterprise Service Bus out there. This blog will cover the basics to get Mule up and running.

Installing Mule

Installation of Mule is pretty straightforward:

  • Download the latest Mule ESB Community Edition here;
  • Extract the downloaded archive to a directory of your choice;
  • If you plan to use Maven, set the MULE_HOME environment variable, eg. in your .bashrc file for Ubuntu:
    export MULE_HOME=~/Applications/mule-standalone-3.1.2
    
  • You can now run the server via the following command (on Ubuntu):
    $MULE_HOME/bin/mule
    

Hello World!

To build a simple Hello World example directly in Eclipse follow this tutorial on the Mule website. It also shows you how to install and use the mule plugin for eclipse.

Using Maven

If you’re like me and like to use Maven, there’s a maven plugin available to quickly create a basic mule standalone project.
The basic command for creating a mule project with the plugin is

mvn mule-project-archetype:create -DartifactId=HelloMule -DmuleVersion=3.1.2

To quickly setup a basic project check out this site. If you follow the example a simple maven project is created.
You can “eclipsify” this project via the following maven command:

mvn eclipse:eclipse

Now, you can import the project in Eclipse.

The generated project

The generated project contains a simple mule configuration:

  <flow name="main">
        <vm:inbound-endpoint path="in" exchange-pattern="request-response"/>
        <echo-component/>
        <vm:outbound-endpoint path="out"/>
    </flow>

As you can see, the example is very simple. It contains two in-memory queues. You can send some text in the vm://in queue and it gets echoed out into the vm://out queue.

Fixing the testcase

Before you can run the example, there’s an error in the generated test case, that needs to be solved first (or you can skip the test when running maven of course).
The configuration for the test case, called artifactId-functional-test-config.xml is similar to the main configuration:

    <flow name="main">
        <vm:inbound-endpoint path="in" exchange-pattern="request-response"/>
        <test:component appendString=" Received"/>
        <vm:outbound-endpoint path="out"/>
    </flow>

As an extra bonus it appends ” Received” to the input string before it is sent to the vm://out queue.
The unit test contains the following code:

    public void testHelloMule() throws Exception
    {
        MuleClient client = new MuleClient(muleContext);
        MuleMessage result = client.send("vm://in", "some data", null);
        assertNotNull(result);
        assertNull(result.getExceptionPayload());
        assertFalse(result.getPayload() instanceof NullPayload);
        assertEquals("some data Received", result.getPayloadAsString());
    }

If you run the test goal, this test case will fail on line 7. The problem is that the input string is sent to the vm://out queue, hence the synchronous result is an empty mule message. To fix this, you can either change the configuration, so that the input is not passed to the vm://out queue, like this:

    <flow name="main">
        <vm:inbound-endpoint path="in" exchange-pattern="request-response"/>
        <test:component appendString=" Received"/>
    </flow>

The other option is to adjust the test case, so that the result is read from the vm://out queue:

    public void testHelloMule() throws Exception
    {
        MuleClient client = new MuleClient(muleContext);
        client.send("vm://in", "some data", null);
        MuleMessage result = client.request("vm://out", 5000);
        assertNotNull(result);
        assertNull(result.getExceptionPayload());
        assertFalse(result.getPayload() instanceof NullPayload);
        assertEquals("some data Received", result.getPayloadAsString());
    }

Installing the mule project

After you fixed the bug in the testcase, you can install the application.

mvn clean install

This will package the project into a zip file and copy this file to the $MULE_HOME/apps directory. Here Mule will pick it up and deploy the file.

For now it doesn’t do much besides creating the 2 in-memory queues. In the next blog, I’ll dive a little deeper into Mule and show some basic configurations.

References

As a small reference, there’s a DZone refcard available containing an overview of Mule 3 commands.

Advertisements

Maven resource filtering not working

I recently ran into a problem when using maven resource filtering from within Eclipse. The problem was that properties in xml files (in this case a spring configuration file) weren’t expanded. Eventually i figured out what was causing the problem. That’s what this small blog is about.

Setup

As noted i wanted maven to expand some properties – datasource properties in this case – in a spring configuration file. The idea was to externalize the datasource connection information in various environment files, so that during deployment the right datasource connection information would be put in the spring configuration file according to the deployment environment, eg. local, test, staging.

For this i altered the spring conguration file, called spring-config.xml in my case, and changed the configuration for the dataSource spring bean into the one resembling the xml stanza below:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
    <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
    <value>jdbc:oracle:thin:@${db.host}:${db.port}:${db.sid}</value>
  </property>
  <property name="username">
    <value>${db.user}</value>
  </property>
  <property name="password">
    <value>${db.password}</value>
  </property>
</bean>

This file was stored in the default resource directory src/main/resources.

The property files were put in the src/main/filters directory and were called filter-<env>.properties, eg. the filter-local.properties file looked like this:

#spring-config.xml
db.host=localhost
db.port=1521
db.sid=XE
db.user=scott
db.password=tiger

In the pom file i added the following code necessary for the property expanding:

<build>
  <filters>
    <filter>src/main/filters/filter_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>
....
<profiles>
  <profile>
    <id>env-local</id>
    <properties>
      <env>local</env>
    </properties>
  </profile>
  <profile>
    <id>env-test</id>
    <properties>
      <env>test</env>
    </properties>
  </profile>
....
</profiles>

Now i used eclipse with the m2eclipse plugin and defined the following configuration for packaging the project (a web application) to a war file suitable for my local environment:

Notice the embedded Maven Runtime which is selected by default.

Problem

After running the configuration the properties for the datasource bean were not expanded and the original spring configuration file for the local deployment got packaged. I also noticed that maven properties in property text files as opposed to xml files, did get expanded. So the problem only occurred in xml files.

Solution

I noticed that running the package maven goal from a terminal window did expand the properties. So eventually i realized that the embedded maven version selected by default was the cause of distress. After i switched the maven runtime for the run configuration to the maven version currently installed on my ubuntu distribution, the problem was solved. See the picture below for this configuration.

The maven version on my ubuntu distribution, by the way, was installed via the following command:

sudo apt-get install maven2

Note that in future versions the bug in the maven embedded runtime may be fixed.

Sortable and clickable lists with Apache Wicket

For my recent project I had to implement a sortable and clickable list. The project uses Apache Wicket as the front-end. It took me a while to get it all working and after some refactoring I was able to produce a generic solution to this problem. This solution is based on the following assumptions

  • The list to be sorted is based on a JPA entity and all the columns that need to be sortable are available as simple getter methods on the entity;
  • The entity has an id attribute, which uniquely identifies it;
  • The equals() and hasCode() methods of the entity will be overriden. Two entitiy instances will  be considered equal if their ids match;
  • The JavaScript jQuery library is available in the Wicket page displaying the list.

See the image below for an example of the eventual sortable and clickable list on which this blog is based.

The list shows the Account entity. All the columns are sortable and clicking on a row will open the url on which the hyperlink of the first column is based. There’s also a pagination link available in case the number of rows exceeds the number of rows displayed. In the remainder of this blog I’ll walk through all the necessary components.

Model

Interface ISortableEntity

For an entity to be able to be sorted in a list, I’ve defined the following interface:


package com.example.entity;

import java.math.BigDecimal;

public interface ISortableEntity {

 public BigDecimal getId();
 public void setId(BigDecimal id);
 public ISortableEntity newSearchInstance();

}

As you can see, the entity has to have an id attribute. It also has to implement the newSearchInstance() method which basically has to return a new instance of the entity class.

Entity Account

The list is based on an entity called com.example.entity.Account. The entity has to implement the ISortableEntity interface and has to override the equals() and hashCode() methods. As mentioned before two entities are considered equal if their ids match.

package com.example.entity;

import java.math.BigDecimal;
import javax.persistence.*;

@Entity
@Table(name = "ACCOUNTS")
@SequenceGenerator(allocationSize = 1, name = "account_id_generator", sequenceName = "accounts_id_seq")
public class Account implements java.io.Serializable, ISortableEntity {

	private static final long serialVersionUID = -1213397204916173215L;
	private BigDecimal id;
	private BigDecimal accountNumber;
	private String firstName;
	private String lastName;
	private String username;

	public Account() {
	}

	public Account(BigDecimal id) {
		this.id = id;
	}

	public Account(BigDecimal id, BigDecimal accountNumber, String firstName,
			String lastName, String username) {
		this.id = id;
		this.accountNumber = accountNumber;
		this.firstName = firstName;
		this.lastName = lastName;
		this.username = username;
	}

	@Id
	@GeneratedValue(generator = "account_id_generator")
	@Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
	public BigDecimal getId() {
		return this.id;
	}

	public void setId(BigDecimal id) {
		this.id = id;
	}

	@Column(name = "ACCOUNT_NUMBER", precision = 22, scale = 0)
	public BigDecimal getAccountNumber() {
		return this.accountNumber;
	}

	public void setAccountNumber(BigDecimal accountNumber) {
		this.accountNumber = accountNumber;
	}

	@Column(name = "FIRST_NAME", length = 100)
	public String getFirstName() {
		return this.firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	@Column(name = "LAST_NAME", length = 100)
	public String getLastName() {
		return this.lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Column(name = "USERNAME", length = 50)
	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Account other = (Account) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	public ISortableEntity newSearchInstance() {
		ISortableEntity searchAccount = new Account();
		return searchAccount;
	}
}

View

For the view layer the sortable list of accounts will be displayed on a Wicket Panel. For this we’ll need a template and the corresponding Java class. For the list to be sortable we first have to extend the abstract class org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider. For one of its methods, we also need to extend the abstract class org.apache.wicket.model.LoadableDetachableModel. I’ve generified both of these subclasses so they only depend on the aforementioned interface ISortableEntity. The code for these classes is shown below.

SortableEntityProvider

package com.example.view.common;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;

import com.example.entity.ISortableEntity;

public class SortableEntityProvider<T extends ISortableEntity> extends
		SortableDataProvider<T> {
	private static final long serialVersionUID = -1646363525627999438L;
	private List<T> data;

	protected List<T> getData() {
		return data;
	}

	protected void setData(List<T> data) {
		this.data = data;
	}

	public SortableEntityProvider(List<T> data, String defaultSort) {
		super();
		setData(data);
		// set default sort
		setSort(defaultSort, true);
	}

	public Iterator<? extends T> iterator(int first, int count) {
		final SortParam sp = getSort();

		Collections.sort(getData(), getComparator(sp));

		return getData().subList(first, first + count).iterator();
	}

	public int size() {
		return getData().size();
	}

	public IModel<T> model(T object) {
		return new DetachableEntityModel<T>(object, getData());
	}

	protected Comparator<T> getComparator(final SortParam sp) {
		return new Comparator<T>() {
			public int compare(T arg0, T arg1) {

				int result;

				PropertyModel<Comparable> model0 = new PropertyModel<Comparable>(
						arg0, sp.getProperty());
				PropertyModel<Comparable> model1 = new PropertyModel<Comparable>(
						arg1, sp.getProperty());

				if (model0.getObject() == null && model1.getObject() == null) {
					result = 0;
				} else if (model0.getObject() == null) {
					result = -1;
				} else if (model1.getObject() == null) {
					result = 1;
				} else {
					result = model0.getObject().compareTo(model1.getObject());
				}

				if (!getSort().isAscending()) {
					result = -result;
				}

				return result;

			}
		};
	}
}

The constructor takes a list with all the rows displayed on the panel (usually a list of entities returned by a service calling a DAO) as the data parameter, along with a string representation of the attribute providing the default sort, eg. “firstName” for default sorting on the First Name of the Account.

DetachableEntityModel

package com.example.view.common;

import java.math.BigDecimal;
import java.util.List;

import org.apache.wicket.model.LoadableDetachableModel;

import com.example.entity.ISortableEntity;

public class DetachableEntityModel<T extends ISortableEntity> extends LoadableDetachableModel<T> {
	private static final long serialVersionUID = -2133620119433783150L;

	private final ISortableEntity entity;
	private final List<T> data;

	public DetachableEntityModel(ISortableEntity a, List<T> data) {
		this.entity = a;
		this.data = data;
	}

	public int hashCode() {
		return getId().hashCode();
	}

	public BigDecimal getId() {
		return entity.getId();
	}

	public boolean equals(final Object obj) {
		if (obj == this) {
			return true;
		} else if (obj == null) {
			return false;
		} else if (obj instanceof DetachableEntityModel) {
			DetachableEntityModel other = (DetachableEntityModel) obj;
			return other.getId() == getId();
		}
		return false;
	}

	protected T load() {
		ISortableEntity searchEntity = entity.newSearchInstance();
		searchEntity.setId(getId());
		return data.get(data.indexOf(searchEntity));
	}
}

AccountsPanel.html

<html>
<body>
<wicket:panel>
  <h2>Accounts</h2>
  <table class="clickable">
    <thead>
      <th wicket:id="orderByAccountNumber">[Account number]</th>
      <th wicket:id="orderByFirstName">[First name]</th>
      <th wicket:id="orderByLastName">[Last name]</th>
      <th wicket:id="orderByUsername">[Username]</th>
    </thead>
    <tbody>
      <tr wicket:id="accountRow">
        <td><a wicket:id="id" href="#"><span wicket:id="idSpan">[id]</span></a></td>
        <td wicket:id="firstName">[First name]</td>
        <td wicket:id="lastName">[Last name]</td>
        <td wicket:id="username">[Username]</td>
      </tr>
    </tbody>
  </table>
  <br />
  <span wicket:id="navigator">[dataview navigator]</span>
</wicket:panel>
</body>
</html>

The css class clickable will take care of the styling of the table and is coupled to the jQuery code (shown later on) that will implement the row clicks. A row click will make sure that the hyperlink coupled to the first column in the table will be clicked. The page also contains a component that will take care of the pagination.

AccountsPanel.java

package com.example.view.common;
....
public class AccountsPanel extends Panel {
 private static final long serialVersionUID = -395786600454932830L;

 @SpringBean
 AccountService accountService;

 public AccountsPanel(String id) {
 super(id);

 drawChildren();
 }

 private void drawChildren() {

 List<Account> accounts = accountService.getAccounts();

 SortableDataProvider<Account> dp = new SortableEntityProvider<Account>(
 accounts, "lastName");

 final DataView<Account> dataView = new DataView<Account>(
 "accountRow", dp) {
 private static final long serialVersionUID = 6364718764586838158L;

 @Override
 public void populateItem(final Item<Account> item) {
 item.add(new SimpleAttributeModifier("class",
 item.getIndex() % 2 == 0 ? "even" : "odd"));

 Link<Account> idLink = new Link<Account>("id") {
 private static final long serialVersionUID = -480222850475280108L;

 @Override
 public void onClick() {
 //TODO: Do stuff
 }
 };
 idLink.add(new Label("idSpan", item.getModelObject().getId()
 .toPlainString()));
 item.add(idLink);

 item.add(new Label("firstName", item.getModelObject()
 .getFirstName()));
 item.add(new Label("lastName", item.getModelObject()
 .getLastName()));
 item.add(new Label("username", item.getModelObject()
 .getUsername()));
 }
 };

 dataView.setItemsPerPage(5);

 addSorting(dp, dataView);

 add(dataView);

 add(new PagingNavigator("navigator", dataView));
 }

 private void addSorting(SortableDataProvider<Account> dp,
 final DataView<Account> dataView) {
 add(new SortableOrderBy<Account>("orderByAccountNumber", "id", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByLastName", "lastName", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByFirstName", "firstName", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByUsername", "username", dp,
 dataView));
 }

}
  • lines 6,7,17: Service (in this case a Spring Bean) that will cough up the list of accounts for display;
  • line 19,20: Here the SortableDataProvider is initialized with the accounts list. The accounts will be sorted on “lastName” by default;
  • line 22,23: The SortableDataProvider is fed to a DataView coupled to the accountRow table row wicket component;
  • line 36: This will be the code executed when the row is clicked;
  • line 52: The list displayed will contain 5 accounts;
  • line 54: Here the table row headers are added. They will be clickable for sorting the corresponding table column they belong to. I’ve created a small subclass for OrderByBorder, called SortableOrderBy so I don’t have to repeat the code that resets the pagination upon sort (the code will be provided below). Note that the second argument of the SortableOrderBy constructor is couple to the getter method on the Account entity. You can also apply sorting to properties of a related entity. Let’s presume that the Account entity has a relationship with an Address entity. If you would like to display say the city of the Address in the Accounts list and make it sortable, the second argument would be address.city ;
  • line 58: Here the paginator for the list is added to the panel.

SortableOrderBy.java

package com.example.view.common;

import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortStateLocator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
import org.apache.wicket.markup.repeater.data.DataView;

public class SortableOrderBy<T> extends OrderByBorder {

  private static final long serialVersionUID = 5017011967951860645L;
  private final DataView<T> dataView;

  public SortableOrderBy(String id, String property,
      ISortStateLocator stateLocator, DataView<T> dataView) {
    super(id, property, stateLocator);
    this.dataView = dataView;
  }

  @Override
  protected void onSortChanged() {
    dataView.setCurrentPage(0);
  }
}

As I said, this is just a simple subclass to reset the current page to the first one after the sorting has been applied.

css

.clickable tr.even {
    background-color:#E9F9FF;
}

.clickable tr:hover {
    background-color: #F0F0F0;
}

.clickable td:hover {
    cursor: pointer;
}

I’m just showing the most important style elements. The above will make sure that:

  • the odd and even rows of the table are of different color;
  • the row that you hover over will change its color;
  • the arrow cursor will be replaced by a pointer cursor when you hover over a clickable row;

jQuery

The last piece of the puzzle is a small piece of jQuery code that will make sure the correct styling is applied and that a row click will lead to the clicking of the corresponding hyperlink programmatically.

$(document).ready(function() {

    $('.clickable tr').click(function() {
        var href = $(this).find("a").attr("href");
        if(href) {
            window.location = href;
        }
    });

})

And that’s all there is to it. With the code shown in this blog you can transform any list of entities into a sortable and clickable one.

Seam-gen and groovy

I recently added a groovy action class to my seam-gen project. After a while eclipse’s auto-build kept giving me these misleading ant errors in the groovy.compilemodel task:

groovyc doesn't support the "srcdir" attribute

It took me a while to fix this issue. When i ran the compile target manually there was no problem, so eventually i figured it had to do with the explode launcher that was generated by seam-gen. You need to let it run in a separate JVM.
First, right-click on the project and select “Properties”.

Properties for open18

Next, select “Builders” and Edit the explode builder.


Edit Configuration

Edit configuration

Finally, click the “JRE” tab en select “Separate JRE” as the runtime JRE.

After this the auto-build error should be gone.

%d bloggers like this: