Tuesday, August 19, 2008

Intercepting HTTP Parameters on an Axis Endpoint in Mule

I've been trying to write an interceptor for Mule 1.4.3 container to intercept a message that contains a username and password for authentication. The interceptor is to act as a Permission Check interceptor described in the Mule docs.

I was able to come up with an approach that might fit my needs, although there could very well likely be a better approach.

My Mule 1.4.3 configuration is setup to take an HTTP request for an Axis endpoint. The HTTP request has a user and a password parameter. I have an interceptor, EchoInterceptor, that is going to look for the user and password parameters:
<mule-configuration id="Mule_Echo_Parameters_Axis" version="1.0">  
<transformers>
<transformer name="HttpRequestToSoapRequest"
className="org.mule.providers.soap.transformers.HttpRequestToSoapRequest"/>
</transformers>
<model name="echoAuth">
<mule-descriptor name="EchoAuth" implementation="EchoAuth">
<inbound-router>
<endpoint address="axis:http://localhost:65081/services"
transformers="HttpRequestToSoapRequest" />
</inbound-router>
<interceptor className="EchoInterceptor"/>
</mule-descriptor>
</model>
</mule-configuration>

The EchoAuth class has an echo method whose invocation I want to intercept. The method simply echos the parameters its given. An HTTP request to the Axis endpoint looks like:
http://localhost:65081/services/EchoAuth?method=echo&user=Costanza&password=bosco

The interceptor class:
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mule.umo.Invocation;
import org.mule.umo.UMOException;
import org.mule.umo.UMOInterceptor;
import org.mule.umo.UMOMessage;
import org.mule.util.PropertiesUtils;

public class EchoInterceptor implements UMOInterceptor {

private final Log logger = LogFactory.getLog(this.getClass());

@Override
public UMOMessage intercept(Invocation invocation) throws UMOException {

logger.info("Intercepted method invocation.");

UMOMessage incomingMessage = invocation.getMessage();
logger.info("Incoming message: " + incomingMessage.toString());

String request = incomingMessage.getProperty("http.request").toString();
int i = request.indexOf('?');
String query = request.substring(i + 1);
logger.info("HTTP request query string: " + query);

// following example in Mule's HTTPRequestToSoapRequest class
Properties p = PropertiesUtils.getPropertiesFromQueryString(query);
String user = p.getProperty("user");
String password = p.getProperty("password");
logger.info("Query string parameter 'user': " + user);
logger.info("Query string parameter 'password': " + password);

UMOMessage resultingMessage = invocation.execute();
logger.info("Returning resulting message: " + resultingMessage.toString());

return resultingMessage;
}
}
The results in my mule.log, with a lot of the clutter omitted:
EchoInterceptor: Incoming message: org.mule.providers.soap.axis.AxisMessageAdapter
/org.mule.providers.soap.axis.AxisMessageAdapter... properties={
http.version=HTTP/1.1
method=public abstract java.lang.String AuthService.echo(java.lang.String,java.lang.String)
MULE_ORIGINATING_ENDPOINT=endpoint.http.localhost.65081.services
http.method=GET
http.request=/services/EchoAuth?method=echo&user=Costanza&password=bosco
}}
EchoInterceptor: HTTP request query string: method=echo&user=Costanza&password=bosco
EchoInterceptor: Query string parameter 'user': Costanza
EchoInterceptor: Query string parameter 'password': bosco
EchoAuth: Received user: Costanza
EchoAuth: Received password: bosco
EchoAuth: Authentication: Costanza/bosco
EchoInterceptor: Returning resulting message:
...
http.request=/services/EchoAuth?method=echo&user=Costanza&password=bosco
http.method=GET
...
In my real-world project, I need to send the user and password off to a
service for authentication, and halt processing. The interceptor doc lists a couple of methods for doing so I need to investigate further.

Saturday, August 16, 2008

Getting Groovy with Apache Derby

I'm working my way through the so-far excellent Programming Groovy by Venkat Subramaniam. Since I'm on the database chapter, I decided it was time to install a database server on my machine. The examples in the book are done with MySQL, but I thought I would try the more lightweight Apache Derby.

I downloaded the 10.4.1.3 binary distribution:
$ tar xzf db-derby-10.4.1.3-bin.tar.gz -C /opt
$ export DERBY_HOME=/opt/db-derby-10.4.1.3-bin
$ java -jar $DERBY_HOME/lib/derbyrun.jar server start &
Security manager installed using the Basic server security policy.
Apache Derby Network Server - 10.4.1.3 - (648739) started and ready to accept connections on port 1527 at 2008-08-16 17:31:37.060 GMT
That was simple enough, I decided to create a couple aliases to make startup and shutdown easier:
alias startderby="java -jar $DERBY_HOME/lib/derbyrun.jar server start &"
alias stopderby="java -jar $DERBY_HOME/lib/derbyrun.jar server shutdown &"
Running Derby's ij tool, I was able to create a database with a script similar to the one in the book, but tailored for Derby:
connect 'jdbc:derby://localhost:1527/peopledb;create=true;';

create table people (
name varchar(100) not null,
age integer not null
);

insert into people (name, age) values ('John', 26);
insert into people (name, age) values ('Jenny', 23);
insert into people (name, age) values ('Marshall', 6);
I could not find a method for determining whether or not a table exists before trying to create it in Derby. I ran the script with:
$ java -jar $DERBY_HOME/lib/derbyrun.jar ij ~/tmp/people.sql

ij version 10.4
ij> connect 'jdbc:derby://localhost:1527/peopledb;create=true;';
ij> create table people (
name varchar(100) not null,
age integer not null
);
0 rows inserted/updated/deleted
ij> insert into people (name, age) values ('John', 26);
1 row inserted/updated/deleted
ij> insert into people (name, age) values ('Jenny', 23);
1 row inserted/updated/deleted
ij> insert into people (name, age) values ('Marshall', 6);
1 row inserted/updated/deleted
I was able to query the database with the following Groovy code (again modeled from the book):
import groovy.sql.Sql

def url = 'jdbc:derby://localhost:1527/peopledb'
def driver = 'org.apache.derby.jdbc.ClientDriver'

def sql = Sql.newInstance(url, driver)
// println sql.connection.dump()

sql.eachRow('SELECT * from people') { person ->
printf "%-20s%s\n", person.name, person.age
}
Command and results:
$ groovy -cp $DERBY_HOME/lib/derbyclient.jar People.groovy
John 26
Pam 24
Marshall 6
Looks like I'm good to continue with the examples..

As an aside, the 10.4.1.3 download page also links for the Eclipse plugins. I've used the plugins for a 10.2 version of Derby for Eclipse 3.3, but not the 10.4 plugins with Eclipse 3.4 which I currently have. I could not get the plugins recognized in Eclipse 3.4, if someone has, if someone has, a comment would be greatly appreciated.

Sunday, August 3, 2008

My Attempt at Bulding Mule 1.4.x

I thought I'd take a crack at building the Mule source, which uses Maven for its builds:
$ svn export http://svn.codehaus.org/mule/branches/mule-1.4.x
$ cd mule-1.4.x
$ mvn validate
I was first presented with the following error:
Missing:
----------
1) org.apache.maven.wagon:wagon-webdav:jar:RELEASE
Added the Mule repositories:
<repository>
<id>muleforge</id>
<url>http://repository.muleforge.org</url>
<name>MuleForge Repository</name>
</repository>
<repository>
<id>mule.dependencies</id>
<url>http://dist.codehaus.org/mule/dependencies/maven2/</url>
<name>Mule Dependencies Repository</name>
</repository>
<!-- For the Abdera dependencies -->
<repository>
<id>apache-incubating</id>
<name>Apache Incubating Repository</name>
<url>http://people.apache.org/repo/m2-incubating-repository/</url>
</repository>
Now the version issue for wagon-webdav was resolved. From the mvn validate output:
    WAGON_VERSION: 1.0-beta-2
My next problem was that I did an export and not a checkout:
[INFO] Updating project files from SCM: skipped.
Provider message:
The svn command failed.
Command output:
svn: '.' is not a working copy

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Cannot get the revision information from the scm repository :
Error!
So I replaced my export with a checkout:
$ svn co http://svn.codehaus.org/mule/branches/mule-1.4.x .
Now a Java version problem:
[WARNING] Rule 1: org.apache.maven.plugin.enforcer.RequireJavaVersion failed with message: Detected JDK Version: 1.6.0
is not in the allowed range [1.4.2,1.5.0).
As I only have Java 6 on my machine:
$ java -version
java version "1.6.0"
OpenJDK Runtime Environment (build 1.6.0-b09)
OpenJDK Server VM (build 1.6.0-b09, mixed mode)
I changed the configuration of the enforcer plugin to allow up to Java 1.6.0:
<requireJavaVersion>
<!-- minimum version is 1.4.2, Java 5 and higher not allowed -->
<!-- <version>[1.4.2,1.5.0)</version> -->
<version>[1.4.2,1.6.0]</version>
</requireJavaVersion>
The Build Guide lists I could of skipped the enforcer with:
$ mvn -DskipEnforcer=true
After running:
$ mvn test
I got a lot farther, but the amount of test errors are kind of scary:
Tests run: 131, Failures: 10, Errors: 46, Skipped: 0
A common error was a found in the output files in tests/functional/target/surefire-reports was:
org.mule.test.config.MuleClasspathConfigBuilderTestCase.txt:org.mule.config.ConfigurationException:
Failed to parse configuration resource
"file:/home/prystasj/workspace/mule-1.4.x/tests/functional/target/test-classes/test-xml-mule-config.xml"
I was able to read the file with Groovy's XmlSlurper just fine:
#!/usr/bin/env groovy
file = new File(args[0])
def xml = new groovy.util.XmlSlurper().parse(file)
println xml
I tried the build with skipping the tests:
$ mvn -DskipTests package
But I got a compilation error. Maybe someday I'll take the time to figure out whats going wrong. I was still able to generate some Eclipse project files though:
$ mvn eclipse:eclipse
So I can still take a good look at the source, which is good enough.

Friday, August 1, 2008

Declarative Spring AOP and Interfaces

I ran into what seems to be a somewhat frequent user error adding Spring AOP through a container when trying to add advice to a class that implements an interface. The exception:
PropertyAccessException 1: org.springframework.beans.TypeMismatchException:
Failed to convert property value of type [$Proxy0] to required type [UpperCaser] for property
'uppercaser'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of
type [$Proxy0] to required type [UpperCaser] for property 'uppercaser':
no matching editors or conversion strategy found" not found in container
Since I ran into the problem and saw a fair share of related posts on the Spring Forums, I thought I'd document it. In my test project, I have a simple class that converts a String to all upper-case letters. Before implementing an interface, everything worked fine.
public class UpperCaser {
public String convert(String toConvert) {
return toConvert.toUpperCase();
}
}
My entry class:
public class UpperCaserUMO  { 
UpperCaser uppercaser;
public String doit(String payload) {
String result = uppercaser.convert(payload);
return result;
}

// needed so Spring can inject uppercaser, can't say:
// UpperCaser caser = new UpperCaser();
public void setUppercaser(UpperCaser uppercaser) {
this.uppercaser = uppercaser;
}
}
And my advice the reverse the upper-cased Sting:
public class UpperCaserAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Object response = invocation.proceed();
String reversedResponse = new StringBuffer((String)response).reverse().toString();
return reversedResponse;
}
}
Everything worked fine with this configuration (my container is Mule 1.4.3):
<bean id="targetUpperCaser" class="UpperCaser" singleton="false"/>
<bean id="upperCaserAdvice" class="UpperCaserAdvice" singleton="false"/>

<bean id="upperCaserUMO" class="UpperCaserUMO" singleton="false">
<spring-property name="uppercaser">
<ref local="upperCaserProxy"/>
</spring-property>
</bean>

<bean id="upperCaserProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
singleton="false">
<spring-property name="target">
<ref local="targetUpperCaser"/>
</spring-property>
<spring-property name="interceptorNames">
<spring-list>
<value>upperCaserAdvisor</value>
</spring-list>
</spring-property>
</bean>

<bean id="upperCaserAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
singleton="false">
<spring-property name="advice">
<ref local="upperCaserAdvice"/>
</spring-property>
<spring-property name="mappedNames">
<spring-list>
<value>convert</value>
</spring-list>
</spring-property>
</bean>
In my "at work" project, the target class, UpperCaser here, implemented an interface, so I added an interface to UpperCaser:
public interface Converter {
public String convert(String toConvert);
}

public class UpperCaser implements Converter {
...
}
I resolved the exception above by changing my entry class to have members of the interface and not the implementation:
public class UpperCaserUMO  { 
Converter uppercaser;
...
public void setUppercaser(Converter uppercaser) {
this.uppercaser = uppercaser;
}
}
And changing my ProxyFactoryBean to acknowledge the interface with the proxyInterfaces property to set things up right took care of the exception:
<bean id="upperCaserProxy" 
class="org.springframework.aop.framework.ProxyFactoryBean"
singleton="false">
<!-- added after adding Converter interface -->
<spring-property name="proxyInterfaces">
<spring-list>
<value>Converter</value>
</spring-list>
</spring-property>
<spring-property name="target">
<ref local="targetUpperCaser"/>
</spring-property>
<spring-property name="interceptorNames">
<spring-list>
<value>upperCaserAdvisor</value>
</spring-list>
</spring-property>
</bean>
And all was well. I based my test project from Mule's Echo example. I also added logging statments which I omitted above so I could track what's going on.
INFO  2008-08-01 12:55:20,063 [WrapperSimpleAppMain] org.mule.MuleServer: Mule Server initialized.
Please enter something:
hello

INFO 2008-08-01 12:55:26,568 [UpperCaserUMO.2] UpperCaserUMO: Inside method doit().
INFO 2008-08-01 12:55:26,575 [UpperCaserUMO.2] UpperCaserAdvice: About to invoke method: convert
INFO 2008-08-01 12:55:26,575 [UpperCaserUMO.2] UpperCaser: Received: hello
INFO 2008-08-01 12:55:26,575 [UpperCaserUMO.2] UpperCaser: Result: HELLO
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserAdvice: Method 'convert' complete.
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserAdvice: Method response: HELLO
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserAdvice: Reversing response...
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserAdvice: Reversed response: OLLEH
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserAdvice: Returning reversed response...
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserUMO: Conversion result: OLLEH
INFO 2008-08-01 12:55:26,576 [UpperCaserUMO.2] UpperCaserUMO: Returning result.
INFO 2008-08-01 12:55:26,593 [SystemStreamConnector.dispatcher.1] org.mule.providers.stream.StreamMessageDispatcher: Connected: StreamMessageDispatcher{this=14fcd9a, endpoint=stream://System.out}

OLLEH
I entered a string in the consloe, UpperCaser upper-cased it, and the advice class reversed it.
Seems a whole lot less complicated to me now. Hopefully this helps someone out in a similar situation.

Pro Spring 2.5
This example was with version 2.0.6 of Spring that comes with Mule 1.4.3. I'm going to assume it works with 2.5.