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.

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.

Setup a local smtp server for JBoss

In my Seam application I needed a local smtp server for testing the email facilities Seam offers. After some research on the internet I came up with a configuration which uses Ubuntu’s Mail Transfer Agent postfix with Gmail as the so called smarthost. Postfix will act as an intermediate smtp server which will dispatch the mails it receives to Gmail‘s smtp server.
This blog will summarize the steps needed for this setup.

Postfix installation and configuration

First, let’s install postfix.

sudo apt-get install postfix

Next, add additional configuration.

sudo dpkg-reconfigure postfix

Choose the following settings. For settings not listed below, you can keep the default settings.

  • General type of mail configuration: Internet with smarthost
  • SMTP relay host: smtp.gmail.com:587
  • Internet protocols to use: all

Next add settings necessary for authentication against the Gmail smtp server.

sudo postconf -e 'smtp_use_tls = yes'
sudo postconf -e 'smtp_sasl_auth_enable = yes'
sudo postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
sudo postconf -e 'smtp_sasl_security_options = noanonymous'
sudo postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Next add the password file /etc/postfix/sasl_passwd containing the username and password of your Gmail account.

smtp.gmail.com username@gmail.com:PASSWORD

Now, hash the password file (this will create the hashed sasl_passwd.db file), throw away the original file and restart the postfix daemon.

sudo postmap /etc/postfix/sasl_passwd
sudo rm /etc/postfix/sasl_passwd
sudo /etc/init.d/postfix restart

Testing postfix

You can now test the postfix configuration via telnet.

telnet localhost 25
>mail from:noreply@gmail.com
>rcpt to:username@gmail.com
>data
>Testmail
>.
>quit

This should send a small email to username@gmail.com.

Troubleshooting

If the mail didn’t arrive, you can check a couple of things:
Check if it’s in the postfix queue:

sudo mailq

Flush the queue:

sudo postfix -f

Check the log file:

sudo tail /var/log/mail.log

JBoss configuration

Next we’re gonna add the mail server configuration to JBoss, so it van be accessed via JNDI. Edit the file $JBOSS_HOME/server/default/deploy/mail-service.xml.

<?xml version="1.0" encoding="UTF-8"?>
<server>
  <mbean code="org.jboss.mail.MailService"
         name="jboss:service=Mail">
    <attribute name="JNDIName">java:/Mail</attribute>
    <attribute name="Configuration">
      <configuration>
        <!-- Change to your mail server prototocol -->
        <property name="mail.transport.protocol" value="smtp"/>
        <!-- Change to the SMTP gateway server -->
        <property name="mail.smtp.host" value="localhost"/>
        <!-- The mail server port -->
        <property name="mail.smtp.port" value="25"/>
        <!-- Change to the address mail will be from  -->
        <property name="mail.from" value="noreply@gmail.com"/>
        <!-- Enable debugging output from the javamail classes -->
        <property name="mail.debug" value="false"/>
      </configuration>
    </attribute>
    <depends>jboss:service=Naming</depends>
  </mbean>
</server>

Seam configuration

Finally we need to configure seam so it can communicate with our new smtp server. This is done in the component descriptor components.xml.

<?xml version="1.0" encoding="UTF-8"?>
<components
  xmlns="http://jboss.com/products/seam/components"
  xmlns:mail="http://jboss.com/products/seam/mail"
  xsi:schemaLocation="http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.2.xsd
    http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">
  ....
  <mail:mail-session session-jndi-name="java:/Mail" />
  ....
</components>

If you’re also using jBPM for business processes within your seam application and need email facilities, you can configure the smtp server in the jBPM configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration>
  ....
  <string name="jbpm.mail.smtp.host" value="localhost"/>
  <string name="jbpm.mail.from.address" value="noreply@open18.org"/>
</jbpm-configuration>

Adding Maven to seam-gen

Recently I changed a seam-gen generated project into a maven project. It took me a while to get it all up and running. In this blog I’ll try to summarize all the steps needed to convert a seam-gen app (the open18 app I created in the previous blogs) into a full blown Maven project called open18_mvn2. I took the liberty to stretch the Maven pom to support deployment to glassfish as well as jboss.

1. Prerequisites

Above is the list with prerequisites. Most of them are taken care of when you’ve followed all the steps in the previous blog series “Seam-gen on glassfish”.

  • You’ve created the seam-gen project open18;
  • You’ll have a mysql database up and running in which resides the open18 catalog with application data.
  • You’ll have a glassfish installation available in ~/opt/glassfishv2;
    • The necessary hibernate and slf4j/log4j libraries and the mysql-connector-java-5.1.12-bin.jar file are all available in the <glassfish_home>/domains/domain1/lib/ext directory. This can be done via the ant task gf-prepare of the seam-gen app;
    • A connection pool (open18Pool) together with a datasource (open18Datasource) for connecting to the mysql open18 catalog is available in the glassfish server. This datasource can be installed on the server via the ant task gf-deploy-datasource of the seam-gen app;
  • For this blog I added a jboss server installation and put it in ~/opt/jboss-5.1.0.GA;
    • See the next section for installation details;
    • The mysql driver mysql-connector-java-5.1.12-bin.jar is available in the <jboss_home>/server/default/lib directory;
    • A datasource file open18-ds.xml is available in the jboss server directory <jboss_home>/server/default/deploy for connecting to the mysql open18 catalog. This datasource can be installed on the server via the ant task datasource of the seam-gen app.

2. Installing JBoss server

  • First download the latest JBoss 5 release here;
  • Unzip the zipfile into your ~/opt folder;
  • For hot deployment to work you need to alter the file <jboss_home>/server/default/conf/bootstrap/profile.xml. Uncomment the following line
          <value>WEB-INF/dev</value>
  • To prevent any memory issues from occurring make sure you start jboss with the following vm arguments (you can do this by adding the jboss server to your Netbeans servers):
-Xms128m -Xmx512m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=512m -Xverify:none

3. Project structure

Next we’ll setup the maven project structure, wherein we will copy the necessary files from the seam-gen app.

open18_mvn2 project structure

  • src
    • main
      • hot: Folder for the hot deployable sources (i.e. the action classes). In the seam-gen app this is the src/hot folder;
      • java: Folder for the ordinary java sources (eg. the model classes). In the seam-gen app this is the src/main folder;
      • resources: All the resource files. In the seam-gen app this is the resources folder without the WEB-INF folder. Of all the multiple environment files, I only added the -dev files and renamed them by dropping the –dev extension. For example, i’ve renamed the components-dev.properties to components.properties.;
      • webap: The web-app folder containing all the view files. In the seam-gen app this is the view folder combined with the resources/WEB-INF folder;
    • test
      • bootstrap: The bootstrap files needed for testing with jboss-embedded. In the seam-gen app this is the bootstrap folder;
      • java: Folder for the test classes. In the seam-gen app this is the src/test folder;
      • resources: Resource files for testing. In the seam-gen app these are the resources/META-INF/persistence-test.xml, resources/components-test.properties and resources/import-test.sql files. I’ve renamed them by dropping off the -test extension.

4. pom.xml

This is the pom file with which it’ll be possible to eg. test and explode the application to glassfish and jboss. I’ve highlighted the lines which I will comment upon in this section.

<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.open18</groupId>
    <artifactId>open18_mvn2</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>open18_mvn2 JEE5 Webapp</name>
    <url>http://maven.apache.org</url>

    <repositories>
        <repository>
            <id>jboss</id>
            <name>JBoss Release Repository</name>
            <url>http://repository.jboss.org/maven2</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>ctpjava</id>
            <name>CTP Public Repository</name>
            <url>http://ctpjava.googlecode.com/svn/trunk/repository</url>
        </pluginRepository>
        <pluginRepository>
            <id>repository.jboss.org</id>
            <name>JBoss Repository</name>
            <url>http://repository.jboss.org/maven2</url>
        </pluginRepository>
        <pluginRepository>
            <id>maven.java.net</id>
            <name>Java.net Maven2 Repository</name>
            <url>http://download.java.net/maven/2</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>

        <!-- *************** Build Dependencies *************** -->

        <dependency>
            <groupId>org.jboss.seam.embedded</groupId>
            <artifactId>hibernate-all</artifactId>
            <version>${jboss.embedded.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- *************** Test Dependencies *************** -->

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>5.8</version>
            <classifier>jdk15</classifier>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam.embedded</groupId>
            <artifactId>jboss-embedded-all</artifactId>
            <version>${jboss.embedded.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam.embedded</groupId>
            <artifactId>jboss-embedded-api</artifactId>
            <version>${jboss.embedded.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam.embedded</groupId>
            <artifactId>thirdparty-all</artifactId>
            <version>${jboss.embedded.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>${javax.faces.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>${javax.faces.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>el-api</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>

        <!-- *************** Seam Dependencies *************** -->

        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam</artifactId>
            <version>${jboss.seam.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-ui</artifactId>
            <version>${jboss.seam.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.jboss.seam</groupId>
                    <artifactId>jboss-seam-jul</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-beanutils</groupId>
                    <artifactId>commons-beanutils</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-ioc</artifactId>
            <version>${jboss.seam.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-debug</artifactId>
            <version>${jboss.seam.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-mail</artifactId>
            <version>${jboss.seam.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-remoting</artifactId>
            <version>${jboss.seam.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>dom4j</groupId>
                    <artifactId>dom4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jboss.seam</groupId>
            <artifactId>jboss-seam-pdf</artifactId>
            <version>${jboss.seam.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.lowagie</groupId>
                    <artifactId>itext</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.lowagie</groupId>
                    <artifactId>itext-rtf</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- *************** RichFaces Dependency *************** -->

        <dependency>
            <groupId>org.richfaces.ui</groupId>
            <artifactId>richfaces-ui</artifactId>
            <version>${jboss.richfaces.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-collections</groupId>
                    <artifactId>commons-collections</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.richfaces.framework</groupId>
            <artifactId>richfaces-api</artifactId>
            <version>${jboss.richfaces.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-collections</groupId>
                    <artifactId>commons-collections</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.richfaces.framework</groupId>
            <artifactId>richfaces-impl</artifactId>
            <version>${jboss.richfaces.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.richfaces.samples</groupId>
            <artifactId>glassX</artifactId>
            <version>${jboss.richfaces.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>jstl</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>nekohtml</groupId>
                    <artifactId>nekohtml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- *************** Drools / jBPM Dependency *************** -->

        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>${drools.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>xerces</groupId>
                    <artifactId>xercesImpl</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>xml-apis</groupId>
                    <artifactId>xml-apis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>janino</groupId>
                    <artifactId>janino</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jbpm</groupId>
            <artifactId>jbpm-jpdl</artifactId>
            <version>${jboss.jbpm-jpdl.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- *************** Facelets Dependency *************** -->

        <dependency>
            <groupId>com.sun.facelets</groupId>
            <artifactId>jsf-facelets</artifactId>
            <version>1.1.15</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>META-INF/orm.xml</include>
                    <include>META-INF/persistence.xml</include>
                    <include>messages*.properties</include>
                    <include>seam.properties</include>
                    <include>components.properties</include>
                    <include>security.drl</include>
                </includes>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
            <testResource>
                <directory>src/test/bootstrap</directory>
            </testResource>
            <testResource>
                <directory>src/main/webapp</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.source.version}</source>
                    <target>${java.source.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${java.source.hotdeploy}</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <childDelegation>true</childDelegation>
                    <useSystemClassLoader>true</useSystemClassLoader>
                    <argLine>
                        -Dsun.lang.ClassLoader.allowArraySyntax=true
                    </argLine>
                </configuration>
            </plugin>
            <plugin>
                <!-- run with 'mvn cli:execute-phase' and use 'hot' -->
                <groupId>org.twdata.maven</groupId>
                <artifactId>maven-cli-plugin</artifactId>
                <configuration>
                    <userAliases>
                        <hot>hotdeploy:exploded -o -Pjboss.local</hot>
                    </userAliases>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <id>copy-test-persistence</id>
                        <phase>process-test-resources</phase>
                        <configuration>
                            <tasks>
                                <!--backup the "proper" persistence.xml-->
                                <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.bck"/>
                                <!--replace the "build" persistence.xml with the "test" version-->
                                <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>restore-persistence</id>
                        <phase>prepare-package</phase>
                        <configuration>
                            <tasks>
                                <!--restore the "build" persistence.xml-->
                                <move file="${project.build.outputDirectory}/META-INF/persistence.xml.bck" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>jboss.local</id>
            <build>
                <defaultGoal>hotdeploy:exploded</defaultGoal>
                <plugins>
                    <plugin>
                        <groupId>com.ctp.seam.maven</groupId>
                        <artifactId>maven-hotdeploy-plugin</artifactId>
                        <version>0.3.1</version>
                        <configuration>
                            <source>${java.source.version}</source>
                            <target>${java.source.version}</target>
                            <sourceDirectory>${java.source.hotdeploy}</sourceDirectory>
                            <deployDirectory>
                                ${directory.deploy.jboss}/${build.finalName}.${project.packaging}
                            </deployDirectory>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-clean-plugin</artifactId>
                        <configuration>
                            <filesets>
                                <fileset>
                                    <directory>${directory.deploy.jboss}</directory>
                                    <includes>
                                        <include>**/${build.finalName}.${project.packaging}*</include>
                                    </includes>
                                    <followSymlinks>false</followSymlinks>
                                </fileset>
                            </filesets>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
            <properties>
                <directory.deploy.jboss>~/Programming/opt/jboss-5.1.0.GA/server/default/deploy</directory.deploy.jboss>
            </properties>
        </profile>

        <profile>
            <id>glassfish.local</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-war-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>Prepare WAR</id>
                                <phase>prepare-package</phase>
                                <goals>
                                    <goal>exploded</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>exec-maven-plugin</artifactId>
                        <version>1.1</version>
                        <executions>
                            <execution>
                                <id>explode to glassfish</id>
                                <phase>prepare-package</phase>
                                <goals>
                                    <goal>exec</goal>
                                </goals>
                                <configuration>
                                    <executable>${glassfish.home}/bin/asadmin</executable>
                                    <arguments>
                                        <argument>deploy</argument>
                                        <argument>${project.build.directory}/${project.build.finalName}</argument>
                                    </arguments>
                                </configuration>
                            </execution>
                            <execution>
                                <id>clean and unexplode</id>
                                <phase>clean</phase>
                                <goals>
                                    <goal>exec</goal>
                                </goals>
                                <configuration>
                                    <executable>${glassfish.home}/bin/asadmin</executable>
                                    <arguments>
                                        <argument>undeploy</argument>
                                        <argument>${project.build.finalName}</argument>
                                    </arguments>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
            <properties>
                <glassfish.home>~/Programming/opt/glassfishv2</glassfish.home>
            </properties>
        </profile>
    </profiles>

    <properties>
        <java.source.version>1.5</java.source.version>
        <java.source.hotdeploy>${basedir}/src/main/hot</java.source.hotdeploy>
        <jboss.seam.version>2.2.1.CR1</jboss.seam.version>
        <jboss.richfaces.version>3.3.3.CR1</jboss.richfaces.version>
        <jboss.jbpm-jpdl.version>3.2.3</jboss.jbpm-jpdl.version>
        <jboss.embedded.version>beta3.SP12</jboss.embedded.version>
        <drools.version>5.0.1</drools.version>
        <javax.faces.version>1.2_13</javax.faces.version>
    </properties>

</project>

line 292-302: testResources

For testing purposes, you need

  • the webapp directory: this includes for one the facelets and page desciptors;
  • the test resources directory: this  will contain files like persistence.xml and components.properties;
  • the test bootstrap directory: this will contain the bootstrap files needed for testing with jboss embedded server.

line 330-340: maven-surefire-plugin

Make sure to add this plugin configuration, or else the tests will surely fail.

line 351-384: maven-antrun-plugin

This is a little workaround. JBoss embedded unfortunately doesn’t see the persistence.xml file in the target/test-classes/META-INF directory. It has to be in the target/classes/META-INF directory. To work around this issue, we’ll make a backup of the persistence.xml in the classes tree and replace it with the test-classes version. After the testing phase we’ll put the backed up persistence.xml file back in place.

line 390: jboss.local profile

Profile for hot deployment and undeployment to jboss.

line 425: directory.deploy.jboss

Make sure to point this to the domain folder of your jboss installation.

line 430: glassfish.local profile

Profile for hot deployment and undeployment to glassfish.

line 484: glassfish.home

Make sure to point this to your glassfish installation.

At this point your project in Netbeans should look like the picture below

open_mvn2 project in Netbeans

5. Test class example

To check if testing (and especially persistence against the Hypersonic database) works correctly I’ve added the test class org.open18.test.CreateNewFacilityActionTest, which adds a new facility. Note that you have to be logged in first to be able to make any permanent changes:

package org.open18.test;

import java.util.List;

import javax.faces.application.FacesMessage;

import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.mock.SeamTest;
import org.testng.annotations.Test;

public class CreateNewFacilityActionTest extends SeamTest {

    @Test(groups = { "level.integration", "speed.slow" })
    public void createNewFacilityTest() throws Exception {

        new FacesRequest("/login.xhtml") {

            @Override
            protected void updateModelValues() {

                setValue("#{credentials.username}", "admin");
            }

            @Override
            protected void invokeApplication() {
                Object outcome = invokeMethod("#{identity.login}");
                assert outcome != null && outcome.equals("loggedIn");
            }

            @Override
            protected void renderResponse() throws Exception {
                   List<FacesMessage> messages = FacesMessages.instance()
                        .getCurrentGlobalMessages();
                assert messages.size() == 1;
                assert messages.get(0).getSeverity().equals(
                        FacesMessage.SEVERITY_INFO);
                assert messages.get(0).getSummary().contains("admin");
            }
        }.run();

        new FacesRequest("/FacilityEdit.xhtml") {

            @Override
            protected void updateModelValues() {
                setValue("#{facilityHome.instance.name}", "Eindhoven golf");
                setValue("#{facilityHome.instance.type}", "PUBLIC");
            }

            @Override
            protected void invokeApplication() {
                Object outcome = invokeMethod("#{facilityHome.persist}");
                assert outcome != null && outcome.equals("persisted");
            }

            @Override
            protected void renderResponse() throws Exception {
                List<FacesMessage> messages = FacesMessages.instance()
                        .getCurrentGlobalMessages();
                assert messages.size() == 1;
                assert messages.get(0).getSeverity().equals(
                        FacesMessage.SEVERITY_INFO);
                assert(messages.get(0).getSummary().contains("Successfully created"));

            }
        }.run();
    }
}

This is the test suite file FacilityIntegrationActionTest.xml that goes along with it:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >

<suite name="RegisterAction Tests" verbose="2" parallel="false">
    <test name="RegisterAction Test">
        <classes>
            <class name="org.open18.test.RegisterGolferIntegrationActionTest" />
        </classes>
    </test>
    <test name="CreateNewFacility Test">
        <classes>
            <class name="org.open18.test.CreateNewFacilityActionTest" />
        </classes>
    </test>
</suite>

6. Other adjustments

For simplicity I didn’t bother to convert the @ant-variable@ replacement in the component.properties file to Maven, so I just altered the file and put the correct hard-coded values in it.

src/main/resources/components.properties

jndiPattern=open18/#{ejbName}/local
debug=true
seamBootstrapsPu=false
seamEmfRef=#{null}
puJndiName=java:comp/env/open18/pu

There are also a couple of files that need to be adjusted for the test goal to work.

src/test/resources/META-INF/persistence.xml

Make sure the following hibernate properties are set with the values shown below

         <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
         <property name="jboss.entity.manager.factory.jndi.name"
                value="java:/entityManager"/>

bootstrap/deploy/hsqldb-ds.xml

Because the generated JPA classes all contain a catalog named “OPEN18” you need to make sure that when Hibernate is going to create the tables in the Hypersonic database you’re connected to a schema by the same name. If you don’t do this, you’ll get a bunch of “invalid schema name OPEN18” messages when running the maven test goal. Add the following line below the displayed comment

      <!-- sql to call when connection is created -->
        <new-connection-sql>CREATE SCHEMA OPEN18 AUTHORIZATION DBA</new-connection-sql>

7. Running the goals

Now you can test your Maven seam-gen app.

  • Test (Netbeans context menu) will run the maven test goal;
  • Run (Netbeans context menu) and selecting a server will deploy the application;
  • Clean (Netbeans context menu) will run the maven clean goal. When you’ve set one of the profiles (via the Set Configuration context menu) it will also undeploy the application from the server;
  • Profile jboss.local (via Custom goals context menu) will hot deploy the application to jboss;
  • Goal prepare-package with profile glassfish.local (via Custom goals context menu) will hot deploy the application to glassfish.

Seam-gen on Glassfish – part III

In this last part we’re gonna create the seam-gen project, import it in NetBeans, deploy it and at the end we have a running application.

1. Create the project

Creating the skeleton of the seam-gen project is done by issuing the following seam-gen command

$ ./seam create-project

If you stuck to the defaults there will be a project directory called open18 in your home directory.

2. Generate the JPA classes

Next we’ll generate the JPA classes from the tables in the open18 database into the new project. Again this is done by a seam-gen command

$ ./seam generate

3. Import in NetBeans

Now let’s import the project into NetBeans. It should look like the picture below

open18 project

The build targets for the NetBeans project are coupled to the default (i.e. JBoss) deploy targets. Let’s change them to their Glassfish counterparts. Right click on the open18 project and choose properties. Change the targets so they’ll look like this

open 18 build properties

4. Change build-glassfish.xml

This is the build file that contains all the Glassfish specific Ant targets. One thing it does, is copying all the Hibernate files to the lib/ext folder of the Glassfish domain (Glassfish uses EclipseLink as its default JPA provider).

We’re gonna make 2 changes to this file.

First we’re gonna make sure that the slf4j/log4j libraries also get copied to the Glassfish domain. If you omit this step, the application won’t deploy properly. Add the following xml stanza to the gf-deploy-hibernate target in the glassfish-build.xml file so that the copy task which copies the libraries to Glassfish resembles the example below.

        <copy todir="${glassfish.domains.dir}/${glassfish.domain}/lib/ext" overwrite="true">
            <fileset dir="${basedir}/lib">
                ....
                <!-- Include slf4j/log4j JARs below -->
                <include name="jcl-over-slf4j.jar"/>
                <include name="slf4j-api.jar"/>
                <include name="slf4j-log4j12.jar"/>
                <include name="log4j.jar"/>
            </fileset>
        </copy>

Next let’s fix a small bug in the deployment of the libraries to a Glassfish v2 server. If you don’t change this the asm.jar, asm-attrs.jar, cglib.jar and cglib-nodep.jar won’t be copied into the Glassfish domain.

Alter the lines in the gf-deploy-hibernate target that check whether the Glassfish server is a version 2  or 3.

<condition property="glassfish.v3" value="true" else="false">
    <available file="${glassfish.home}/glassfish" type="dir"/>
</condition>
<condition property="glassfish.domains.dir" value="${glassfish.home}/glassfish/domains" else="${glassfish.home}/domains">
    <istrue value="${glassfish.v3}"/>
</condition>

5. Prepare Glassfish for Hibernate

Now let’s copy all those library files to the Glassfish domain. Right click the build.xml file in the NetBeans project and choose Run Target > gf-prepare.

One last thing that needs to be taken care of, is fixing a bug in the Seam code regarding Hibernate and mysql. If you don’t do this, the facelet TeeList.xhtml (which corresponds with the Tee List menu item in the open18 application) will give you the following error message

/TeeList.xhtml: javax.persistence.PersistenceException: org.hibernate.exception.DataException: could not execute query

A nice workaround is provided here. In this workaround the flawed Seam component org.jboss.seam.persistence.persistenceProvider is overriden with the following Seam component (which has a higher precedence)

package org.open18.persistence;

import javax.annotation.PostConstruct;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
import org.jboss.seam.persistence.HibernatePersistenceProvider;

/**
 * Addresses problem with HibernatePersistenceProvider by explicitly
 * calling HibernatePersistenceProvider.init() from
 * a PostConstruct method that actually gets called on postconstruct.
 *
 */
@Name("org.jboss.seam.persistence.persistenceProvider")
@Scope(ScopeType.STATELESS)
@BypassInterceptors
@Install(classDependencies={"org.hibernate.Session", "javax.persistence.EntityManager"})
public class MoBetterHibernatePersistenceProvider extends HibernatePersistenceProvider {

    private static Log log = Logging.getLog(MoBetterHibernatePersistenceProvider.class);

    @PostConstruct
    public void init()
    {
       log.debug("MoBetterPersistenceProvider init");
       super.init();
    }

}

6. Deploy the application

We’re almost there. Let’s deploy the application.

  • Right click the build.xml file and choose Run Target > gf-start to startup the Glassfish server;
  • Right click the open18 project and choose Clean and Build.

7. Run the application

Finally! You should be able to play with the open18 seam-gen app which runs on the following url: http://localhost:8080/open18.

Seam-gen on Glassfish – part II

In this second part we’re gonna setup an example database, prepare Glassfish for mysql and finally run seam setup to create the proper build.properties file for deployment to glassfish. I’m using the example schema provided by the excellent Seam in Action book by Manning. The setup scripts can be found in the zip file located here.

To recap I’m using the following file structure as recommended in the Seam in Action book:

  • ~/lib: this will contain the mysql driver, which will be copied by seam-gen to the Glassfish server;
  • ~/opt: this already contains jboss-seam and the Glassfish server;
  • ~/open18: this will contain the seam-gen generated project.

1. Installing the database schema

First download the seaminaction-20090413.zip file and extract it in your home directory.

Now login to the mysql database

$ mysql -u <user_name> -p

Now create a database and run the mysql scripts contained in the zip file

sql> CREATE DATABASE open18;
sql> USE open18;
sql> source ~/seaminaction/etc/schema/open18-initial-schema-mysql.sql
sql> source ~/seaminaction/etc/schema/open18-initial-seed-data-mysql.sql

2. Download and install the mysql driver

Next we’ll gonna provide seam-gen with a recent mysql driver

  • First, download the MySQL Connector/J zip file here.
  • Unzip this file and copy the mysql-connector-java-5.1.12-bin.jar to your ~/lib directory

The jar will be picked up by seam-gen later on and copied to the Glassfish domain. You can also choose to copy the driver file directly to the Glassfish server by copying the jar file to <glassfish_home>/domain/domain1/lib/ext (assuming you’ll be deploying the seam-gen app to this default domain).

3. Running seam-gen setup

Now it’s time to run seam-gen setup. This will create the proper build.properties file needed for the creation the the NetBeans project later on.

Switch to ~/opt/jboss-seam-2.2.1.CR1 and enter

$ ./seam setup

Answer the questions and verifiy the generated build.properties file in the ~/opt//jboss-seam-2.2.1.CR1/seam-gen directory. It should look like this:

seam-gen build.properties

Now all the hard work is done, in the last part of this blog series, we’re gonna create the seam-gen project and deploy it to our Glassfish domain!!

Seam-gen on Glassfish – part I

I recently managed to get a seam-gen app running on Glassfish. To share how this was done, i’ve divided this blog in 3 parts. This part will handle most of the prerequisite steps.

The complete setup

  • Ubuntu 9.10
  • Mysql server 5.1
  • Glassfish v2.1.1
  • Jboss Seam 2.2.1.CR1
  • Netbeans 6.8
  • Ant 1.7.1
  • Subversion 1.6.5

Jboss Seam and the Glassfish server will be installed under ~/opt.

1. Install JDK 6

First of all you need to install a recent JDK version

$ sudo apt-get update
$ sudo apt-get install sun-java6-jdk

Note: if you’re using ubuntu10 you need to enable the “partner” repository first. This is done via the System > Administration > Software menu.

2. Install Mysql Server 5.1

Next we’re gonna install mysql server on ubuntu

$sudo apt-get install mysql-server

3. Install Subversion (optional)

For versioning support we’ll be using subversion with http support

$ sudo apt-get install subversion
$ sudo apt-get install apache2
$ sudo apt-get install libapache2-svn

3a. Create repository

Next we’re gonna create a svn repository

$ sudo svnadmin create /var/svn/repos

3b. Edit svn configuration

Edit the subversion configuration

$ sudo gedit /etc/apache2/mods-enabled/dav_svn.conf

Alter this so that effectively when ignoring all comments it’ll look like this

<Location /svn>
    DAV svn
    SVNPath /var/svn/repos
    AuthType Basic
    AuthName "Subversion Repository"
    AuthUserFile /etc/apache2/dav_svn.passwd
</Location>

3c. Create a svn user

We need a user to connect to the svn repository

$ sudo htpasswd -cm /etc/apache2/dav_svn.passwd <user_name>

When prompted enter a pasword.

3d. Restart Apache

$ sudo /etc/init.d/apache2 restart

3e. Change repository owner and rights

You need to change the owner and add some group rights on the repository files, or else you’ll get an error when commiting changes tot the repository over http.

$ sudo chown www-data:www-data  /var/svn/repos -R
$ sudo chmod g+w /var/svn/repos -R

Finally you can test if you can connect to your repository via the url http://127.0.0.1/svn

4. Install Netbeans 6.8

Download the installer file Netbeans

$ chmod +x <installer-file-name>.sh
$ sudo ./<installer-file-name>.sh

Accept the defaults. Netbeans will be installed in the /usr/local directory.

5. Install Glassfish 2.1.1

Download the installer file Glassfish v2 into a temporary directory

$ java -Xmx256m -jar <installer-file-name>.jar
$ mv glassfish ~/opt/glassfishv2
$ cd ~/opt/glassfishv2
$ chmod -R +x lib/ant/bin
$ ./lib/ant/bin/ant -f setup.xml

Note: To prevent future problems when the jdk version get updated make sure, that glassfish uses the symbolic link to the jdk installation.
The JDK home is set in the file <glassfish_home>/config/asenv.conf. It should read:

AS_JAVA="/usr/lib/jvm/java-6-sun/jre/.."

6. Install Jboss-seam

Download the latest seam distibution from the Seam website and unzip it into ~/opt/

7. Install Ant

$sudo apt-get install ant

If you’ve come this far, you”ll have all the components you need to get started with seam-gen!!

%d bloggers like this: