Recently there was a post on the icefaces forum www.icefaces.org/JForum/posts/list/10052.page that asked the question why are so many developers unaware of java messaging.. In the same post the poster talked about the difficulties of getting activemq messaging working with icefaces.
Almost a year ago when I was learning jsf and icefaces I came across a acegi security demo , the demo in addition to illustrating spring security also demonstrated basic ondemand rendering . Curiously enough the demo was a simulation of a jms queue active monitor.
Now with much greater knowledge of icefaces and jsf I have thought it might be useful to create an actual activemq real-time monitor.
In this first part of this article we will discuss the design of the active monitoring routines, in later articles we will go into some depth of integrating with icefaces. To get to a good start lets look at some some code. as always this code is available in the projects SVN repositary.
methodology (this is subject to change)
In concept the activeMqMonitor is quite simple, there is a single jsf page with an icefaces expandable table component, a Backing bean and a singleton to maintain the state. This structure is very similar to the icefaces auctionMonitor , which is one of the most useful icefaces sample programs I've seen. Upon instantatiation the backing bean will launch one or more ActiveMonitor threads. The Monitors will watch for queue additons or messages being added to discovered queues. When queues or messages are discovered a new message will be formualted and wil be added to a discoveredQueues topic. This should simplify the coding as only one queue needs to be monitored by the backing bean. A messageListener on the backing beans will watch for messages added to the queues topic, When the messageListener gets a message it will trigger an onDemandRender on the UserComponent. Pushing the changes out to all users, much like the icefaces security demo showed.
Again I encourage all to play with the icefaces samples in the src and bin packages, there is a wealth of techniques to lean, more next time.
package blog.mdp;
import
java.io.BufferedReader;
import
java.io.IOException;
import
java.io.InputStreamReader;
import
java.util.Iterator;import java.util.Set;
import
javax.jms.Connection;
import
javax.jms.JMSException;import javax.jms.Queue;
import
org.apache.activemq.ActiveMQConnectionFactory;
import
org.apache.activemq.advisory.DestinationSource;
import
org.apache.activemq.command.ActiveMQQueue;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.jms.core.JmsTemplate;
public class RegistrationConsole {
public static void main(String[] args) throws IOException {
ApplicationContext context =
new ClassPathXmlApplicationContext("/blog/mdp/client-context.xml");JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");
BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
for (;;) {System.out.print("To Register, Enter Name: ");
String name = reader.readLine();
RegistrationRequest request = new RegistrationRequest(name);
jmsTemplate.convertAndSend(request);
ActiveMonitor activeMonitor = new ActiveMonitor();
// testGetAllQueues.getAllQueues();
Thread m = new Thread(activeMonitor);
m.start();
}
}
}
And this is the active queue monitor
package blog.mdp;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.QueueSession;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.advisory.DestinationSource;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.filter.DestinationMap;
import org.apache.activemq.advisory.DestinationEvent;
import org.apache.activemq.advisory.DestinationListener;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.broker.region.DestinationStatistics;
public class ActiveMonitor implements Runnable {
public ActiveMonitor() {
}
public void getAllQueues() {
}
public void run() {
final String BROKER_URL = "tcp://localhost:61616";
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
BROKER_URL);
Connection connection;
Session session;
while (true) {
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
connection.start();
DestinationSource ds = new DestinationSource(connection);
ds.start();
ds.setDestinationListener(new DestinationListener() {
public void onDestinationEvent(DestinationEvent event) {
try {
ActiveMQDestination destination = event
.getDestination();
if (destination instanceof ActiveMQQueue) {
ActiveMQQueue queue = (ActiveMQQueue) destination;
}
} catch (Exception e) {
}
}
});
Set<ActiveMQQueue> queues = ds.getQueues();
for (ActiveMQQueue queue : queues) {
try {
System.out
.println("-----------------------------------------------------");
System.out.println("..queue: " + queue.getQueueName());
System.out.println("..queue is topic : "
+ queue.isTopic());
System.out.println("..queue is queue : "
+ queue.isQueue());
System.out.println("..toString: " + queue.toString());
System.out.println("..props: "
+ queue.getProperties().toString());
;
QueueBrowser browser = session.createBrowser(queue);
Enumeration stat = browser.getEnumeration();
if (stat.hasMoreElements()) {
while (stat.hasMoreElements()) {
Message message = (Message) stat.nextElement();
System.out.println("message : " + message);
}
} else {
System.out.println("no messages in queue");
}
System.out
.println("-----------------------------------------------------");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
connection.close();
connection = null;
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
1) The name of the Demo Application is ActiveMonitor , the code so far is available in the svn.
2) Operational notes:
-
On startup the application will scan for any local network servers with activeMq.
-
Broadcast an dns-sd identity
-
Network hosts defined in the properties file
-
Servers on Local host
-
Other instances of ActiveMonitor broadcasting a dns-sd (bounjour) identity.
-
Identified Local servers will be added to the table along with any discovered queues.
-
Discovered queues are added to the unexpanded child table.
-
Each queue line willl show the message headers and message count.
-
Clicking the browse button will allow for browsing an individual queue, results will display in a popup . or child object.
-
An active Monitor thread will be started , the thread will watch for new dns-sd servers , queues or messages added to existing queues
Here is the spring configuration
<?xml version="1.0" encoding="UTF-8"?>
<
beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance<?xml version="1.0" encoding="UTF-8"?> <xmlns="http://www.springframework.org/schema/beans"
<xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<beans>
<bean id="renderManager" class="com.icesoft.faces.async.render.RenderManager" scope="singleton" />
<--- the clock bean will help with increment/decrementing users from the renderer -->
<bean id="ClockBean" class="com.mcv.faces.ActiveMonitor.beans.ClockBean" lazy-init="true" scope="request"><property name="renderManager" ref="renderManager"/></bean>
<bean id="ActiveMonitorBean" class="com.mcv.faces.ActiveMonitor.beans.ActiveMonitorBean" lazy-init="true" scope="request"><property name="renderManager" ref="renderManager"/></bean>
<----
Because lazy-init is specified the beans must be instantiated initially in the xhtml, however the tradeoff is faster startup.
<
ice:form id="iceForm">
<div id="hiddenDiv">
<ice:outputText value="#{ActiveMonitorBean.autoLoad}"/>
<ice:outputText value="#{ClockBean.autoLoad}"/>
</div>
---->
</
beans>
In addtion there is also a singleton session componet to maintain the state of the application.