Welcome to my RPi: simple JavaEE exercise with webservice and JMS

So my Raspberry Pi has arrived and I was eager to try out some Java programming on it; actually, I wanted to have a first simple exercise to stress test it with a JavaEE container, which I choose JBoss AS 7. Of course the overall performances are nothing comparable to usual servers where you would normally deploy a JavaEE application, however being for small/home projects it doesn’t seems too bad either in order to get started!

Photo 16-12-12 20 24 38

The goal for this exercise is simple: develop a webservice to be exposed in a JavaEE application, which “spool” the content of the webservice call onto a JMS Queue.

Listed:

  • Rasberry Pi (model B)
  • Java 7 SE for Embedded
  • JBoss AS 7 as the JavaEE container
  • Apache Camel
  • SoapUI to test the webservice
  • Hermes JMS as a consolle to access the JMS Queue

First things first, in order to install the Java SE 7 RE, I overall followed the instructions found here, and also a maybe plain but still interesting video on YouTube of what seems to be a Java User Group session – check out the related James Gosling video as well, it’s not RPi related but a very interesting talk nevertheless from THAT James Gosling!

Anyway, once the JRE is installed on the RPi:
JRE on the RPi

… it’s time to install JBoss AS 7:
Screen Shot 2012-12-16 at 10.49.27

In this case, what I did is a custom standalone.xml configuration file to have all the basics, plus JMS which HornetQ is the implementation for JBoss AS.

Time to code!

With the JBoss Developer Studio IDE (basically Eclipse IDE + JBoss Tools), Maven for a simple webapp, and switch Java Compiler to 1.6:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>

and some simple dependencies:

		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>2.10.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-jms</artifactId>
			<version>2.10.3</version>
		</dependency>

		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>

The former block is to have Apache Camel to simplify as much as possible, while keeping loosely coupled, the integration between the webservice and the JMS, while the latter block is for the JavaEE libraries provided by the JBoss AS container.

Then, time to code the webservice:

@WebService()
public class SpoolOnQueue {

	@EJB
	CamelBootstrap cb;

	@WebMethod()
	public String sayHello(String name) {
	    cb.getProducerTemplate().sendBody("direct:spoolOnJms", name);
	    return "Your message has been spooled on JMS";
	}
}

Which is actually rather simple: it defines a webservice class thanks to the @WebService and related @WebMethod annotation, for a sayHello() method of our interest, in charge of spooling the content of the message onto the JMS queue via the Camel’s sendBody() to a specific route.

There is a CamelBoostrap cb dependency injection, which is the JavaEE component in charge of managing the Camel context and routing, defined as:

@Singleton
@Startup
public class CamelBootstrap {
	private CamelContext camelContext;
	private ProducerTemplate producerTemplate;

	public CamelContext getCamelContext() {
		return camelContext;
	}

	public ProducerTemplate getProducerTemplate() {
		return producerTemplate;
	}

	@PostConstruct
	protected void init() throws Exception {
		camelContext = new DefaultCamelContext();
		camelContext.addRoutes(new RouteBuilder() {
			@Override
			public void configure() throws Exception {
				// in the JMS connection we can use # for the ConnectionFactory because by not using Spring the default for Camel is the JNDIRegistry
				// just remind by default it's implied it's a Queue as per Camel JMS doc
				from("direct:spoolOnJms")
				.log("spoolOnJms: ${body}")
				.to("jms:sample?connectionFactory=#ConnectionFactory");
			}
		});
		camelContext.start();
		producerTemplate = camelContext.createProducerTemplate();
	}
}

The CamelBoostrap therefore is a simple Startup, Singleton JavaEE Bean, in charge of initializing the CamelContext, the Camel’s ProducerTemplate, and the one and only camel route of our interest here:

  • starting at direct:spoolOnJms
  • logging the body content
  • and spooling the body content onto the jms:sample JMS Queue

Time to amend the web.xml configuration file to the 3.0 Servlet specs (meaning as well EJB 3 in this case) and linking the aforementioned SpoolOnQueue webservice class:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <display-name>SpoolOnQueue</display-name>
    <servlet-name>SpoolOnQueue</servlet-name>
    <servlet-class>net.tarilabs.test.SpoolOnQueue</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>SpoolOnQueue</servlet-name>
    <url-pattern>/SpoolOnQueue</url-pattern>
  </servlet-mapping>
</web-app>

Last thing before packaging up and deploy, is to define the JMS Queue, which in the camel route is defined as “sample”; to do so, there are several ways from JBoss consolle, to JBoss CLI, configuration files, etc. and in this case I opted for a deployable configuration file – in the WEB-INF/ directory it’s enough to place a HornetQ configuration file which must be ending with -jms.xml, et voilà!:

<?xml version="1.0" encoding="UTF-8"?>
<messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">
    <hornetq-server>
        <jms-destinations>
         <jms-queue name="sample">
            <entry name="jms/queue/sample"/>
            <entry name="java:jboss/exported/jms/queue/sample"/>
         </jms-queue>
        </jms-destinations>
    </hornetq-server>
</messaging-deployment>

Note to self: in this case the -jms.xml HornetQ configuration file is to be placed in the WEB-INF/ directory because the application is packaged as a WAR, otherwise it should have been placed in the usual META-INF/ directory.

Time to deploy:
Screen Shot 2012-12-16 at 20.35.48

You may want to note the deployment takes longer if compared with today’s server or machine where you normally test and deploy these artifacts, however it’s not so bad for home project just reminds me the performance of my old Pentium II with JBoss 3 (or 5? can’t remember) with the difference that nowadays JavaEE 6 and JBoss AS 7 simplify and improve things by great extent.

Then, I use SoapUI to consume the SpoolOnQueue webservice with a call for a simple “Ciao here is some content to place on JMS. Matteo.” message:
Screen Shot 2012-12-16 at 20.37.47

In the JBoss log there is a line now for the Camel route started and spooling the body content on the JMS Queue.

Then, time to connect HermesJMS to this remote HornetQ / JBoss AS, as I hope to have contributed here:

When I start I can see the AS7 I can see in the log the RemoteConnectionFactory JNDI, but this cannot be seen in the console:9990 because of some bugs. However still:
12:47:16,653 INFO [org.jboss.as.messaging] (pool-4-thread-2) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory. With reference to the aforementioned -jms.xml HornetQ configuration file, from a REMOTE perspective: the RemoteConnectionFactory is on java:jboss/exported/jms/RemoteConnectionFactory, the Queue is on java:jboss/exported/jms/queue/sample

Therefore to configure HermesJMS

Step 1: Create a classpath group for HornetQ in HermesJMS
Only one JAR is needed, is the jboss-client.jar inside the directory bin\client of the JBoss AS 7 / JBoss EAP 6 directory

Step 2: Configure a HornetQ session in HermesJMS:
Class: hermes.JNDIContextFactory
Loader: HornetQ – the one defined in step #1
Properties:
binding=jms/RemoteConnectionFactory
initialContextFactory=org.jboss.naming.remote.client.InitialContextFactory
providerURL=remote://localhost:4447
securityPrincipal=MYUSERNAME
securityCredentials=MYPASSWORD
urlPkgPrefixes=org.jboss.naming.remote.client

Step 3: In the Destionations of the HornetQ session which you are defining as part of step#2, Add a new Desitionation
Name: jms/queue/sample
Domain: QUEUE

Now in HermesJMS in the left tree structure you have your Sessions > Your Session > Queue which you can double-click to connect to. You want to notice that for the binding you set the RemoteConnectionFactory without the java:jboss/exported prefix, and the same goes for the Queue defined, where from java:jboss/exported/jms/queue/sample I stripped away the java:jboss/exported prefix. Also notice that you can define the username/password either via the (securityPrincipal,securityCredentials) properties as above, or either use the “Connection” settings fields (User, Password) at the bottom of the Session Preference page of step#2.

So because the webservice has been consumed, which in turn started the Camel route, to spool on the JMS Queue:
Screen Shot 2012-12-16 at 20.38.12

The message now appears on the “sample” JMS Queue which is now inspected thanks to the HermesJMS consolle application.

Why do I blog this

I wanted to have a first “stress” test exercise with my new Raspberry Pi, not only to develop with some Java on it, but using a recent JavaEE container: the performances are not the best, still requires time to deploy or to compile .jsp pages (more in following posts) however I do believe for small/home projects the Rapsberry Pi is a very interesting, cheap both in terms of money and power consumptions, and cool platform where to deploy simple JavaEE applications!
ps: code available on github.

3 thoughts on “Welcome to my RPi: simple JavaEE exercise with webservice and JMS”

  1. I followed the instructions but it did not work. An exception is thrown:

    2013-06-19 11:04:21,423 [Hermes ThreadPool-1] DEBUG hermes.JNDIContextFactory – properties: {java.naming.provider.url=remote://localhost:4447, java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory, java.naming.factory.url.pkgs=org.jboss.naming.remote.client, java.naming.security.principal=jms, java.naming.security.credentials=hornetq}
    2013-06-19 11:04:21,492 [Remoting “config-based-naming-client-endpoint” read-1] ERROR org.jboss.remoting.remote.connection – JBREM000200: Remote connection failed: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed
    2013-06-19 11:04:21,495 [Hermes ThreadPool-1] ERROR hermes.JNDIContextFactory – javax.naming.NamingException: Failed to create remoting connection
    javax.naming.NamingException: Failed to create remoting connection [Root exception is java.lang.RuntimeException: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed]
    at org.jboss.naming.remote.client.ClientUtil.namingException(ClientUtil.java:36)
    at org.jboss.naming.remote.client.InitialContextFactory.getInitialContext(InitialContextFactory.java:121)
    at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
    at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
    at javax.naming.InitialContext.init(InitialContext.java:242)
    at javax.naming.InitialContext.(InitialContext.java:216)
    at hermes.JNDIContextFactory.createContext(JNDIContextFactory.java:261)
    at hermes.JNDIConnectionFactory._getConnectionFactory(JNDIConnectionFactory.java:39)
    at hermes.ext.hornetq.HornetQAdminFactory.createSession(HornetQAdminFactory.java:58)
    at hermes.impl.HermesAdminAdapter.getAdmin(HermesAdminAdapter.java:64)
    at hermes.impl.HermesAdminAdapter.discoverDestinationConfigs(HermesAdminAdapter.java:82)
    at hermes.impl.DefaultHermesImpl.discoverDestinationConfigs(DefaultHermesImpl.java:1126)
    at hermes.browser.tasks.DiscoverDestinationsTask.invoke(DiscoverDestinationsTask.java:77)
    at hermes.browser.tasks.TaskSupport.run(TaskSupport.java:175)
    at hermes.browser.tasks.ThreadPool.run(ThreadPool.java:170)
    at java.lang.Thread.run(Thread.java:722)
    Caused by: java.lang.RuntimeException: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed
    at org.jboss.naming.remote.protocol.IoFutureHelper.get(IoFutureHelper.java:87)
    at org.jboss.naming.remote.client.NamingStoreCache.getRemoteNamingStore(NamingStoreCache.java:56)
    at org.jboss.naming.remote.client.InitialContextFactory.getOrCreateCachedNamingStore(InitialContextFactory.java:166)
    at org.jboss.naming.remote.client.InitialContextFactory.getOrCreateNamingStore(InitialContextFactory.java:139)
    at org.jboss.naming.remote.client.InitialContextFactory.getInitialContext(InitialContextFactory.java:104)
    … 14 more
    Caused by: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed
    at org.jboss.remoting3.remote.ClientConnectionOpenListener$Capabilities.handleEvent(ClientConnectionOpenListener.java:365)
    at org.jboss.remoting3.remote.ClientConnectionOpenListener$Capabilities.handleEvent(ClientConnectionOpenListener.java:214)
    at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:72)
    at org.xnio.channels.TranslatingSuspendableChannel.handleReadable(TranslatingSuspendableChannel.java:189)
    at org.xnio.channels.TranslatingSuspendableChannel$1.handleEvent(TranslatingSuspendableChannel.java:103)
    at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:72)
    at org.xnio.nio.NioHandle.run(NioHandle.java:90)
    at org.xnio.nio.WorkerThread.run(WorkerThread.java:184)
    at …asynchronous invocation…(Unknown Source)
    at org.jboss.remoting3.EndpointImpl.doConnect(EndpointImpl.java:270)
    at org.jboss.remoting3.EndpointImpl.doConnect(EndpointImpl.java:251)
    at org.jboss.remoting3.EndpointImpl.connect(EndpointImpl.java:349)
    at org.jboss.remoting3.EndpointImpl.connect(EndpointImpl.java:333)
    at org.jboss.naming.remote.client.EndpointCache$EndpointWrapper.connect(EndpointCache.java:105)
    at org.jboss.naming.remote.client.NamingStoreCache.getRemoteNamingStore(NamingStoreCache.java:55)
    … 17 more
    2013-06-19 11:04:21,496 [Hermes ThreadPool-1] ERROR hermes.browser.tasks.HermesBrowserTaskListener – Could not create InitialContext: Failed to create remoting connection
    javax.jms.JMSException: Could not create InitialContext: Failed to create remoting connection
    at hermes.JNDIContextFactory.createContext(JNDIContextFactory.java:282)
    at hermes.JNDIConnectionFactory._getConnectionFactory(JNDIConnectionFactory.java:39)
    at hermes.ext.hornetq.HornetQAdminFactory.createSession(HornetQAdminFactory.java:58)
    at hermes.impl.HermesAdminAdapter.getAdmin(HermesAdminAdapter.java:64)
    at hermes.impl.HermesAdminAdapter.discoverDestinationConfigs(HermesAdminAdapter.java:82)
    at hermes.impl.DefaultHermesImpl.discoverDestinationConfigs(DefaultHermesImpl.java:1126)
    at hermes.browser.tasks.DiscoverDestinationsTask.invoke(DiscoverDestinationsTask.java:77)
    at hermes.browser.tasks.TaskSupport.run(TaskSupport.java:175)
    at hermes.browser.tasks.ThreadPool.run(ThreadPool.java:170)
    at java.lang.Thread.run(Thread.java:722)
    2013-06-19 11:04:21,496 [Hermes ThreadPool-1] DEBUG hermes.browser.tasks.ThreadPool – task hermes.browser.tasks.DiscoverDestinationsTask@16e4c74 stopped

    Like

    1. Ciao Dalí, have you tried connecting and lookup the JNDI from a client application *outside* the container, just to be sure with the same parameters the JNDI lookup works, indeed the JNDI connection is established ok for the parameters you setup?

      Like

Leave a comment