One of the early use cases for axiom was to validate messaging based systems integration over http. The basic premise was that system-a talks to system-b over http (and vice versa) and that some basic tracing would be helpful, along with validation of xsd on the wire. There was also a desire to do the same thing with various other protocols and transports later on.
This is exactly the kind of thing that camel routes are good for, and was part of the inspiration for writing axiom. The long term goal I have is to be able to define this ‘scenario’ using a nice DSL:
# file http_a2b.scenario
scenario 'http://system-a.nat.mycorp.com/' => 'http://system-b.nat.mycorp.com/' do
message_format = 'xml'
message_schema = '/path/to/schema.xsd'
assuming is_not(valid_schema?) { expect response(500) }
assuming "/order[string-length(@uuid) <= 0]" do
expect response(500)
end
assuming 'orderCorrelationId' => missingOrEmpty do
expect response(401) { |resp| resp.body =~ /Order has not been created/ }
end
end
I’d ideally like that code to sit in my project somewhere, and whenever somebody checks in, I’d like my build to run it for me. That’s the aspiration, and it includes generating random test data based on the supplied schema and producing build/test reports that will show up in my CI build status.
That’s the aspiration, but the reality is not nearly so complete. Testing a scenario with the current axiom release is a matter of setting up the route(s) yourself, then dealing with the outputs after the mediation layer has finished its work. Using the latest alpha release of axiom (0.4.5), I knocked up a quick example of how you might do this.
To start with, we’ll need to put the camel-jetty and camel-http jars into the endorsed/lib directory, along with any of their dependencies we don’t already have. After much messing around (a process called ‘discovery’), this led me to copy the following jars into the folder:
./endorsed/lib:
total 2600
drwxr-xr-x 12 pax staff 408 1 Apr 12:29 .
drwxr-xr-x 7 pax staff 238 31 Mar 17:37 ..
-rw-r--r-- 1 pax staff 33240 1 Apr 12:29 camel-http-1.5.0.jar
-rw-r--r-- 1 pax staff 21260 1 Apr 12:29 camel-jetty-1.5.0.jar
-rw-r--r-- 1 pax staff 30085 1 Apr 12:29 commons-codec-1.2.jar
-rw-r--r-- 1 pax staff 305001 1 Apr 12:29 commons-httpclient-3.1.jar
-rw-r--r-- 1 pax staff 66435 1 Apr 12:29 geronimo-servlet_2.4_spec-1.1.1.jar
-rw-r--r-- 1 pax staff 500206 1 Apr 12:29 jetty-6.1.11.jar
-rw-r--r-- 1 pax staff 34409 1 Apr 12:29 jetty-client-6.1.11.jar
-rw-r--r-- 1 pax staff 16900 1 Apr 12:29 jetty-sslengine-6.1.11.jar
-rw-r--r-- 1 pax staff 160524 1 Apr 12:29 jetty-util-6.1.11.jar
-rw-r--r-- 1 pax staff 132430 1 Apr 12:29 servlet-api-2.5-6.1.11.jar
The endorsed directory ($AXIOM_HOME/endorsed) is added to the class path, along with any jars present in the endorsed/lib directory, upon start-up. This makes it possible to use external jars and load scripts (and other resources) quite easily from your own scripts. Plans for dynamic reloading aren’t implemented yet.
After putting these in place, it is time to write up a camel route script to proxy between both http endpoints. The tracing part comes “out of the box” with camel and is just a matter of configuring your logging properties to put the trace output(s) into the correct destination. All we need in addition to this, is a record of any message bodies that didn’t conform to a given schema. Here’s an example file then:
require 'java'
require 'axiom'
require 'axiom/plugins'
route {
xsd_file = "/tmp/http.request.xsd"
logger.debug "reading schema from #{xsd_file}"
intercept(is_not(valid_schema?(xsd_file))).
to("file:///tmp/.badschemas")
from("jetty:http://#{config >> 'http.test.inbound.uri'}").
to("http://#{config >> 'http.test.outbound.uri'}")
}
You’ll need a properties file somewhere too, as the inbound and outbond uris are defined therein. When starting axiom up as a standalone application, you can pass the location of the additional properties file(s) using a system property, like so:
./axiom-server.sh start -Daxiom.configuration.externals=/path/to/file.properties:/more/files.properties
Running this example with the camel and/or axiom logging threshold set to debug will show that camel gets it’s knickers in a twist about the outbound http endpoint not being present. We can get around that with a little ruby script to listen and write all message bodies to standard out:
#!/usr/bin/env ruby
require 'uri'
require 'webrick'
uri = URI.parse('http://localhost:8080/test/outbound')
server = WEBrick::HTTPServer.new 'Port'.to_sym => uri.port
server.mount_proc uri.path do |request, response|
STDOUT.puts request.body
response.status = 200
end
server.start
puts 'done'
This is just a cobbled together example, so we don’t need to do much more to get ourselves ready. First we’ll kick off the ruby listener:
chmod +x listener.rb && ./listener.rb
Finally, once the listener is good to go, we can try testing the route out with curl:
curl http://localhost:8080/test/inbound -d "<request><data /></request>" -H "Content-Type: text/xml" -v
If the first message schema is valid, going to the directory (or file) in which you traced invalid schemas should be empty. Now you can try again with a broken document and (hopefully) see it picking up the messages and putting them in the invalid log file/directory.
Conclusion
What we’ve put together here, is a far cry from where I’d like to be. Using a DSL to describe your systems integration scenario in terms of expectations, and having your CI build execute this against a real test environment whilst generating test reports is my goal. Over the coming weeks however, I hope we’ll be able to see how these basic camel routes can be generated by the kind of DSL code we saw exemplified above, and how we can take the basic idea behind the stand-alone server and encode the same kind of behaviour into plugins for common build tools such as ant and maven.