ActiveMQ and Camel : How to store a Copy of a JMS Message as File

For some testing- and debug scenarios I had the need to store all messages passing the ActiveMQ Broker to the local file system. This enables me to analyse the messages after they were fetched by the consumers. ActiveMQ provides Camel-Support. Camel is a „framework“ for Enterprise Integration Patterns. I struggled a bit with that word, so here just a few capabilities of what camel is and what it supports:

Ok, now how to configure Camel in ActiveMQ:

In the conf/activemq.xml you have to import the camel.xml file:

<import resource="jetty.xml"/>
<import resource="camel.xml"/>

After that, configure the broker in conf/camel.xml. At the end of the file there is a bean id=activemq. Set your broker config there.
I had to change the brokerURL:

<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent" >
	<property name="connectionFactory">
	  <bean class="org.apache.activemq.ActiveMQConnectionFactory">
		<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
		<property name="userName" value="${activemq.username}"/>
		<property name="password" value="${activemq.password}"/>
	  </bean>
	</property>
</bean>

Now have a look at the sample route in camel.xml:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
	<!-- You can use Spring XML syntax to define the routes here using the <route> element -->
	<route>
		<description>Example Camel Route</description>
		<from uri="activemq:example.A"/>
		<to uri="activemq:example.B"/>
	</route>
</camelContext>

This route  forwards a message from Queue example.A to example.B. Now add a multicast Route:

<route>
	<from uri="activemq:queue:a"/>
	<multicast>
		<to uri="activemq:queue:b"/>
		<to uri="activemq:queue:c"/>
		<to uri="activemq:queue:d"/>
	</multicast>
</route>

Let’s assume you would like to store Messages as File:

<route>
	<from uri="activemq:queue:a"/>
	<multicast>
		<to uri="activemq:queue:b"/>
		<to uri="activemq:queue:c"/>
		<to uri="activemq:queue:d"/>
		<to uri="file://C:java/apache-activemq-5.8.0/writeMsgToFile?autoCreate=true"/>
	</multicast>
</route>

The <to uri> expression creates a copy of the message as file in the defined directory. The autoCreate=true parameter creates the directory if it is not already there.

Lets assume you would like to set the file name for each message based on Header Parameters or the date when the message is processed:

<route>
	<from uri="activemq:queue:a"/>
	<multicast>
		<to uri="activemq:queue:b"/>
		<to uri="activemq:queue:c"/>
		<to uri="activemq:queue:d"/>
		<to uri="file://C:java/apache-activemq-5.8.0/writeMsgToFile?fileName=${date:now:yyyyMMdd}backup-${in.header.OrderType}-${date:now:hh.mm.ss.SSS}.txt"/>
	</multicast>
</route>

The ${date:now:yyyyMMdd} creates a new directory every day. The expression ${in.header.<CustomProperty>} allows you to use some custom JMS Property. In my case I have a custom property called „OrderType“. So I know by looking the filename which kind of message is stored. The last expression ${date:now:hh.mm.ss.SSS} inserts a timestamd in every filename. This is necessary if it can happen, that severel messages arrive the broker at the same time. If you do add milliseconds (SSS) to the filename, the old file will be overwritten by the new one which arrives the broker at the same second.

That’s a first, simple introduction on camel and ActiveMQ.

Tomcat: Unresolved compilation problems: The import javax.servlet.Servlet cannot be resolved

During my tests with servlets I got a really confusing error message. After restarting Tomcat I tried to execute the servlet

and got the following screen:

Tomcat Servlet Problem
Tomcat Servlet Problem

I was confused, because the servlet-api.jar was included in the lib folder. To be sure, you can display the loaded classes by -verbose:class swicth.

Fix: Recompile your class in your eclipse environment. There seems to be a  problem which leads sometimes to non proper compilations in eclipse.I was wondering about that, but I found some other blogs / boards where other people faced the same behavior.

Configure ActiveMQ Web console for password secured broker / Queues

In my last post about ActiveMQ I showed how to configure ActiveMQ in order to secure the Queues by Username and Password.

In the last days I noticed, that the web console throws an exception when I try to click on one of the queues. I realized, that the webconsole couldn’t open the queue because it is password protected.

In the logfile there was an exception like this (see the bold words):

2013-08-15 17:09:23,486 | WARN  | Failed to add Connection ID:PBOSS-56722-1376579349017-3:1, reason: java.lang.SecurityException: User name [system] or password is invalid. | org.apache.activemq.broker.TransportConnection | ActiveMQ VMTransport: vm://localhost#1-1
2013-08-15 17:09:23,489 | INFO  | Connector vm://localhost Stopped | org.apache.activemq.broker.TransportConnector | qtp2024674927-37
2013-08-15 17:09:23,490 | WARN  | /admin/browse.jsp | org.eclipse.jetty.servlet.ServletHandler | qtp2024674927-37
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‚queueBrowser‘ defined in ServletContext resource [/WEB-INF/webconsole-query.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.activemq.web.QueueBrowseQuery]: Constructor threw exception; nested exception is javax.jms.JMSException: User name [system] or password is invalid.

Ok, now we see, that the web console seems to use the username „system“ by default. But in my configuration the queues are secured by „superman“ and „boss“. So I have to „tell“ the web console this information. You can define that in the file: <activemq-home>/conf/credential.properties:

#this is the queue User
activemq.username=supermann
#this is the queue password
activemq.password=boss

Now you have to restart MQ and the web console will work fine 🙂

HTTP Request using JSP

In the last days I worked a lot around HTTP Request and Response stuff and here I would like to share some of the stuff.
The following code snippet shows a JSP page that sends an HTTP Request to a specified URL. It is also possible to specify custom headers (see myVeryCoolHeader) and it is also possible to send basix authentication for username/password secured websites:

<%@ page import="java.net.HttpURLConnection"%>
<%@ page import="java.net.URL"%>
<%@ page import="java.io.*" %>
<%
	
	URL myURL= new URL("http://mydummysite.org/testing/website.html");
	HttpURLConnection myconn = (HttpURLConnection) myURL.openConnection();

	String mybody = "This is the HTTP-Body. You can put here any text stuff you want";

	myconn.setRequestMethod("GET");
	myconn.setDoOutput(true);
	myconn.setUseCaches(false);
	myconn.setRequestProperty("myVeryCoolHeader", "My very cool header -value");
	
	
	// if the website requires basic authentication:
	boolean isSecuredWebsite = true;
	if (isSecuredWebsite){
		String login = "myUSER" + ":" + "myPassword";
		String loginEncoded = new sun.misc.BASE64Encoder().encode(login.getBytes());
		myconn.setRequestProperty("Authorization", "Basic " + loginEncoded);
	}
	
	// request the content as html text
	myconn.setRequestProperty("Content-Type", "text/html");
	myconn.setRequestProperty("Content-Length", String.valueOf(mybody.length()));
	
	// write the stuff to the output stream 
	OutputStreamWriter writer = new OutputStreamWriter(myconn.getOutputStream());
	writer.write("nn" + mybody);
	writer.flush();
	
	
	
	if(myconn.getResponseCode()== 200){
		// everyting ok, put the output from SAP into the HTTP Response
		BufferedReader reader = new BufferedReader(new InputStreamReader(myconn.getInputStream()));

		
		// write the stuff to the website
		String line = reader.readLine();
		while (line != null){
			out.println(line);
			line = reader.readLine();
		}
		reader.close();
		
	} else if(myconn.getResponseCode() == 401) {  
		// Catch the "not Authorized" Status code
		out.println("This Website is secured by Username and Password. <br>");
		
		out.println("<b>DEBUG-INFO</b>: <br>" +myconn.getHeaderField("WWW-Authenticate"));
		out.println("<br>URL: " + myURL.toString());
		out.println("<br>HTTP-Response Code: " + myconn.getResponseCode() + " " + myconn.getResponseMessage());


		
	} else{
		// all other errors like redirect, etc.
		out.println("An unexpected error appeared. Please inform your administrator about this issue. <br>");
		out.println("<b>DEBUG-INFO</b>: <br>");
		out.println("<br>URL: " + myURL.toString());
		out.println("<br>HTTP-Response Code: " + myconn.getResponseCode() + " " + myconn.getResponseMessage());
		
	}


	writer.close();
	myconn.disconnect();
	
%>

 

 

Apache Ant – Copy files to ftp server

Sample for copying files to an FTP Server:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" basedir="." default="deploy">
	<target name="deploy">

		<ftp server="myserver.de"
	   port="21"
	   remotedir="/html/testing/"
	   userid="myUser"
	   password="myPassword"
	   passive="yes"
	   depends="yes"
	   binary="no">
			<fileset dir="Testing">
				<include name="*.php"/>
				<include name="*.js"/>
			</fileset>
		</ftp>

	</target>
</project>

By default, ant is not able to process this, because the ftp classes are not included by the typical ant installation.

To enable ant to use ftp tasks, you have to download the apache commons ftp package. The dependencies of Apache Ant can be found here: http://ant.apache.org/manual/Tasks/ftp.html

Place the downlaoded commons-net.jar file into the lib folder of your ant installation. Now you can execute the ant tasks in your cmd shell.

If you have placed the jar-file outside the ant-lib directory you can run the ant-script with the lib-parameter:

ant -lib "/home/philipp/java/libs/commons-net-3.3/commons-net-3.3.jar"

Links:

Apache commons net: http://ant.apache.org/manual/install.html#commons-net

Ant FTP Task: http://ant.apache.org/manual/Tasks/ftp.html