Code I/O

A topnotch WordPress.com site


Leave a comment

5 Minutes on Adobe Flex: Marshalling AS3 Object to XML

Here is a handy method that can quickly convert AS3 Object to XML.

		public static function objectToXML(object:Object, qualifiedName:String):XML
		{
			var qName:QName = new QName(qualifiedName);
			var xmlDocument:XMLDocument = new XMLDocument();
			var simpleXMLEncoder:SimpleXMLEncoder = new SimpleXMLEncoder(xmlDocument);
			var xmlNode:XMLNode = simpleXMLEncoder.encodeValue(object, qName, xmlDocument);

			return new XML(xmlDocument.toString());
		}
Advertisements


Leave a comment

5 Minutes on Java: Creating a JMS producer with Camel and FUSE frameworks

In the previous post I demonstrated how to quickly create a server push application.  However, the previous post deliberately avoided the producer component.  In this post we’ll create a producer totally de-coupled from the consumer. I like this approach because, the clients need not be developed within the producer context.

What you’ll need?

  • Spring: for injecting beans (avoid writing boiler plate code)
  • Apache Camel: enable routing and manage messaging infrastructure
  • FUSE: message broker

Create a Java project.

You’ll require the following jar files.

        org.springframework.asm-3.0.3.RELEASE.jar
        org.springframework.beans-3.0.3.RELEASE.jar
        org.springframework.context-3.0.3.RELEASE.jar
        org.springframework.context.support-3.0.3.RELEASE.jar
        org.springframework.core-3.0.3.RELEASE.jar
        org.springframework.jdbc-3.0.3.RELEASE.jar
        org.springframework.transaction-3.0.3.RELEASE.jar
        log4j-1.2.15.jar
        commons-lang-2.4.jar
        commons-codec-1.4.jar
        commons-logging-1.1.1.jar
        org.springframework.expression-3.0.3.RELEASE.jar
        commons-dbcp-1.4.jar
        commons-pool-1.5.4.jar
        slf4j-log4j12-1.5.10.jar
        slf4j-api-1.5.10.jar
        camel-core-2.4.0.jar
        camel-jetty-2.4.0.jar
        camel-jms-2.2.0.jar
        camel-spring-2.4.0.jar
        commons-discovery-0.2.jar
        commons-io.jar
        commons-management-1.0.jar
        org.springframework.aop-3.0.3.RELEASE.jar
        org.springframework.jms-3.0.3.RELEASE.jar
        activemq-all-5.4.0-fuse-00-00.jar

Create a applicationContext.xml and add the following beans

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jms="http://www.springframework.org/schema/jms"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:s="http://www.springframework.org/s"
	xmlns:context="http://www.springframework.org/schema/context"

	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

	<!-- ActiveMQ JMS -->
	<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
		p:brokerURL="tcp://localhost:61616" />

	<jms:listener-container container-type="default"
		destination-type="topic" connection-factory="connectionFactory"
		acknowledge="auto">
	</jms:listener-container>

	<camelContext xmlns="http://camel.apache.org/schema/spring">
	</camelContext>

	<!-- Producer -->
	<bean id="messageProducer" class="org.onesun.camel.test.MessageProducer" init-method="init">
		<property name="topic" value="jms:topic:flex-client-broadcast-topic" />
	</bean>

	<bean id="clientProxy" class="org.onesun.camel.test.CamelTestApp">
		<property name="messageProducer" ref="messageProducer" />
	</bean>
</beans>

The connectionFactory bean references the FUSE ActiveMQ broker endpoint to which the application must bind to.  the listener container uses the connectionFactory to create a default route to topics and finally, the camelContext activates the beans and the endpoints.  These three definitions are essential for interconnecting FUSE, JMS and Camel, with this we’re now ready to write a simple producer.

package org.onesun.camel.test;

import org.apache.camel.EndpointInject;
import org.apache.camel.InOnly;
import org.apache.camel.ProducerTemplate;
import org.apache.log4j.Logger;

@InOnly
public class MessageProducer {
	private static Logger logger = Logger.getLogger(MessageProducer.class);
	protected String topic = null;

	@EndpointInject
	protected ProducerTemplate producer;

	public void init(){
		logger.info(MessageProducer.class + " initialized");
	}

	public void send(String message) {
		producer.sendBody(topic, message);
	}
	public String getTopic() {
		return topic;
	}
	public void setTopic(String topic) {
		this.topic = topic;
	}
}

The MessageProducer bean provides interface to send message to a topic. Now we can create the main program that will publish messages onto the topic.

package org.onesun.camel.test;

import java.util.Date;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class CamelTestApp {
	private static ApplicationContext context = null;

	private static MessageProducer messageProducer = null;

	public static MessageProducer getMessageProducer() {
		return messageProducer;
	}

	public void setMessageProducer(MessageProducer messageProducer) {
		ClientProxy.messageProducer = messageProducer;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// Load Spring
		if (context == null) {
			context = new ClassPathXmlApplicationContext("applicationContext.xml");
		}

		for(int index = 0; index < 10; index++){
			messageProducer.send(
				"<item><label>" +
				new Date() +
				"</label></item>"
			);
		}
	}
}

With this application running you should see the dates appear on the flex sample or any other consumer application.  Good luck fusing together frameworks and riding the Camel.

This example has been tested and validated on Linux and Windows.


3 Comments

5 Minutes on Adobe Flex: Server Push with FUSE (ActiveMQ) and BlazeDS

Working in a fast paced environment demands everything to be quick.  Adobe Flex has been our choice to create rapid prototypes; In this post, I want to give a step-by-step guide to setup Server Push simulation using FUSE message broker on the server side and BlazeDS on the Adobe Flex side.

Here is what you require to start:

  • Download and install FUSE message broker: Installed at FUSE_HOME (C:progressfuse-message-broker-5.3.1-02-00)
  • Download and install Apache Tomcat: Installed at TOMCAT_HOME (C:Program FilesApache Software FoundationTomcat 5.5)
  • Download BlazeDS: Put the blazeds.war file into TOMCAT_HOME/webapps

You need the following files from FUSE_HOME/lib/web and FUSE_HOME/lib; Copy them into TOMCAT_HOME/shared/lib

activemq-all-5.3.2.jar
activemq-web-5.3.2.jar
commons-dbcp-1.2.2.jar
commons-discovery-0.2.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
commons-management-1.0.jar
commons-pool-1.3.jar
log4j-1.2.13.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.1.jar
spring-aop.jar
spring-jms.2.5.4.jar
spring-ldap-1.2.1.jar
spring-security-core-2.0.4.jar
spring-tx-2.5.4.jar
spring-web-2.5.4.jar

After the primary components are installed and the dependent libraries copied; we’re set to ready to configure the system for server push; Follow the steps in the same order in which it is illustrated to keep it easy.

Create a new file “blazeds.xml” file in TOMCAT_HOME/conf/Catalina/localhost

<?xml version="1.0" encoding="UTF-8"?>
<Context path="blazeds">
    <Resource name="jms/flex/TopicConnectionFactory"
        type="org.apache.activemq.ActiveMQConnectionFactory"
        description="JMS Connection Factory"
        factory="org.apache.activemq.jndi.JNDIReferenceFactory"
        brokerURL="tcp://localhost:61616"
        brokerName="myBroker"/>

    <Resource name="jms/feedMessageTopic"
        type="org.apache.activemq.command.ActiveMQTopic"
        description="a simple topic"
        factory="org.apache.activemq.jndi.JNDIReferenceFactory"
        physicalName="flex-client-broadcast-topic"/>
</Context>

Here, the resources for JMS are defined.   The TopicConnectonFactory is bound to the broker, which is used by BlazeDS to manage the communication.  The feedMessageTopic is the internal BlazeDS reference to a topic named “flex-client-broadcast-topic” which will be created on the ActiveMQ platform.  From the FUSE admin window, we can send a message to topic “flex-client-broadcast-topic” and see that in the flex app.

Making BlazeDS function with JMS: The following files must be updated correctly, observe the topic name, it must be consistent; which is a must to get the server push should work seamlessly.

Edit TOMCAT_HOME/blazeds/WEB-INF/web.xml; add the following lines to web-app container

<resource-ref>
        <description>Connection Factory</description>
        <res-ref-name>jms/flex/ActiveMqConnectionFactory</res-ref-name>
        <res-type>javax.jms.QueueConnectionFactory</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

    <resource-env-ref>
        <resource-env-ref-name>jms/feedMessageTopic</resource-env-ref-name>
        <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
    </resource-env-ref>

The above configuration binds the BlazeDS web-app to the JMS resources created in the context definition.

Edit TOMCAT_HOME/blazeds/WEB-INF/flex/messaging-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service"
    class="flex.messaging.services.MessageService">

    <adapters>
        <adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="false" />
        <adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter" default="true"/>
    </adapters>

    <default-channels>
        <channel ref="my-polling-amf"/>
    </default-channels>

    <destination id="flex-client-broadcast-topic">
        <properties>
            <jms>
                <destination-type>Topic</destination-type>
                <message-type>javax.jms.TextMessage</message-type>
                <connection-factory>java:comp/env/jms/flex/TopicConnectionFactory</connection-factory>
                <destination-jndi-name>java:comp/env/jms/feedMessageTopic</destination-jndi-name>
                <delivery-mode>NON_PERSISTENT</delivery-mode>
                <message-priority>DEFAULT_PRIORITY</message-priority>
                <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
                <initial-context-environment>
                    <property>
                        <name>Context.INITIAL_CONTEXT_FACTORY</name>
                        <value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
                    </property>
                    <property>
                        <name>Context.PROVIDER_URL</name>
                        <value>tcp://localhost:61616</value>
                    </property>
                </initial-context-environment>
            </jms>
        </properties>

        <channels>
		<channel ref="my-polling-amf"/>
		<channel ref="my-streaming-amf"/>
		<channel ref="my-amf"/>
        </channels>

        <adapter ref="jms"/>
    </destination>
</service>

The messaging-config.xml hooks up the BlazeDS and JMS adaptors to work correctly.  Focus on the destination id “flex-client-broadcast-topic” this must match the physical name used during the context definition.  As well, BlazeDS JMS messaging is done via “my-polling-amf“.  This is the channel responsible for streaming content to the flex app, hence it is a must to be defined in “services-config.xml”.

Edit TOMCAT_HOME/blazeds/WEB-INF/flex/services-config.xml; add the following channel information

        <channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
        </channel-definition>

When all the configuration changes are done, we can boot up the processes required for this setup to work, and can see the server push in action.

launch FUSE_HOME/bin/activemq.bat

restart tomcat

open a browser and launch the URL http://localhost:8161/admin/topics.jsp

At this point of time, if you observe the list of topics, you should see the topic name “flex-client-broadcast-topiclisted with 1 consumer connection to it.  If you’ve reached this point, you’re set for the next step.

Create a simple Flex App (FeedClientApp.mxml)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onAppCreated(event)">

<mx:Script>
	<![CDATA[
		/*
		 * SAMPLE PAYLOAD

		<item><label>SAP</label></item>
		*/
		import mx.collections.ArrayCollection;
		import mx.messaging.events.MessageEvent;

		[Bindable]
		private var entries:ArrayCollection = new ArrayCollection();

		private function onAppCreated(event:Event):void{
			consumer.subscribe();
		}

		private function messageHandler(event:MessageEvent):void{
			var xml:XML = new XML(event.message.body as String);
			entries.addItem(xml);
		}

		private function onConnect(event:Event):void{
		}
	]]>
</mx:Script>

	<mx:Consumer id="consumer"
		destination="flex-client-broadcast-topic"
		channelConnect="onConnect(event)"
		message="messageHandler(event)">
	</mx:Consumer>

	<mx:List dataProvider="{entries}" width="100%" height="100%" labelField="label"/>
</mx:Application>

  • Compile and run this piece of code.
  • Go to http://localhost:8161/admin/send.jsp?JMSDestination=flex-client-broadcast-topic&JMSDestinationType=topic
  • Send a message body as follows: <item><label>SAP</label></item>
  • You should now see “SAP” render in the list.

In the next post I’ll show the producer component which can be developed independently.