Icefaces and ActiveMq
Monday, October 13, 2008 9:34 PM

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
    • A discovery event will formulate a  new message
      • the new message will trigger a messagelistener
      • Update counts and add new items to the table
      • rerender the sessionBean pushing the updates to the subscribed userBeans 

Here is the spring configuration

 

<?xml version="1.0" encoding="UTF-8"?>

<beans

xmlns="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.

 

by michelle | with no comments
A case study in expandable tables.
Monday, July 07, 2008 10:12 AM

Portions of this document c@ 2008 mooncatventures.com, released under GPL License.

 

Some of these techniques may be useful, some may be totally wrong, if anything they are a good starting point for conversation.

Problem:

Add additional rows to an existing table .

 Using techniques icefaces.org has demonstrated in both their tutorials and showcase its quite easy to add additional rows of information to an existing tabe such as the one shown below.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

This works reasonably well as long as you are working with homogenious tables, where the child table has the same number of columns and comparable size columns as the parent table.

 

Clicking the triangle next to the item number (column 1) , inserts the"added value column" imeadiately after the clicked row.

 

To acomplish this define the child table within the parent table, and reference the parent table from a session scoped bean.

 Such as below.

  • Define the Inventory Item table, with the childList defined as a variable within the table.
  • Define the actionListener that is the target of your command link within the inventoryList
  • Define and expand and contract method. The expand and contract method inserts the childtable at the index position within the parent Table.
public class InventoryItem {

private boolean isExpanded;

private List<InventoryItem> childList = new ArrayList<InventoryItem>();

private List<InventoryItem> carInventory = new ArrayList<InventoryItem>();

private static final String EXPAND_IMAGE = "./images/triangle_close.gif";

private static final String CONTRACT_IMAGE = "./images/triangle_open.gif"; private String expandContractImage=EXPAND_IMAGE;

// slock number

int stock;

// model or type of inventory

String model;

// description of item

String description;

// number of miles on odometer

int odometer;

// price of car in Canadian dollars

int price;

Boolean
newRow=false;

public Boolean getNewRow() {return newRow;

}

public void setNewRow(Boolean newRow) {this.newRow = newRow;

}

/**

* Creates a new instance of InventoryItem.

* @param stock stock number.

* @param model model or type of inventory.

* @param description description of item.

* @param odometer number of miles on odometer.

* @param price price of care in Canadian dollars.

*/

public InventoryItem(int stock, String model, String description, int odometer, int price) {

this.stock = stock;

this.model = model;

this.description = description;

this.odometer = odometer;

this.price = price;

this.newRow=false;

}

public InventoryItem(int stock, String model, String description, int odometer, int price,boolean newRow) {

this.stock = stock;

this.model = model;

this.description = description;

this.odometer = odometer;

this.price = price;

this.newRow=newRow;

}

 

/**

* Gets the stock number of this iventory item.

* @return stock number.

*/

public int getStock() {return stock;

}

/**

* Gets the model number of this iventory item.

* @return model number.

*/

public String getModel() {return model;

}

/**

* Gets the description of the this iventory item.

* @return description

*/

public String getDescription() {return description;

}

/**

* Gets the odometer reading from this iventory item.

* @return odometer reading.

*/

public int getOdometer() {return odometer;

}

/**

* Gets the price of this item in Canadian Dollars.

* @return price.

*/

public int getPrice() {return price;

}

public void toggleExpandAction(ActionEvent event) {

// toggle expanded state

isExpanded = !isExpanded;

// add sub elements to list

if (isExpanded) {

expandNodeAction();

}

// remove items from list

else {

contractNodeAction();

}

}

private void expandNodeAction() {

FacesContext ctx = FacesContext.getCurrentInstance();

Application app = ctx.getApplication();

TableBean tb = (TableBean)app.createValueBinding(
"#{carList}").getValue(ctx);

int index = tb.getCarInventory().indexOf(this);

this.childList.add(

new InventoryItem(58285, "added value", " This Row was Dynamically added", 43500, 21695,true));

tb.getCarInventory().get(index).setExpandContractImage(
CONTRACT_IMAGE);tb.getCarInventory().addAll(index +1, this.childList);

}

/**

* Utility method to remove all child nodes from the parent dataTable list.

*/

private void contractNodeAction() {if (this.childList != null && this.childList.size() > 0) {

FacesContext ctx = FacesContext.getCurrentInstance();

Application app = ctx.getApplication();

TableBean tb = (TableBean)app.createValueBinding(
"#{carList}").getValue(ctx);

int index = tb.getCarInventory().indexOf(this);

// remove all items in childFilesRecords from the parent list

tb.getCarInventory().get(index).setExpandContractImage(EXPAND_IMAGE);

tb.getCarInventory().removeAll(this.childList);

this.childList.clear();

// this.setExpandedContent(false);

}

}

public String getExpandContractImage() {return expandContractImage;

}

public void setExpandContractImage(String expandContractImage) {this.expandContractImage = expandContractImage;

}

public static String getCONTRACT_IMAGE() {return CONTRACT_IMAGE;

}

public boolean isExpanded() {return isExpanded;

}

public void setExpanded(boolean isExpanded) {this.isExpanded = isExpanded;

}

}

 

But Look what happens when the columns are of different lengths, the column in the parent table expands to accomodate the child rows. This may not be very desirable from a UI perspective.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Larger screenshot

 

One way around this might be to define all your headers and columns inside ONE actual column in the parent table, using a panelGrid rather than individual <ice:column> tags.

 

 

 

 

 

 

 

 

 

 

 

 

 

Larger screenShot

Another technique that works considerably well is to display the added rows in an iframe , The following code will generate th effect below.

<ice:panelGroup rendered="#{item.expandRow}">

<iframe width="100%" height="350px" align="top" ALLOWTRANSPARENCY="true" STYLE="position:absolute; overflow:none; left:0; z-index:1;display:block"

frameBorder="0" src="styledTable.iface"></iframe>

</ice:panelGroup>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Larger ScreenShot

Larger ScreenShot

Its a simple matter to add some blank rows to fill in the panel below the iframe, even more dramatic effect can be acheived with a little CSS manipulation.

 

 

 

 

 

Larger ScreenShot

 

 

 

 

 

 

 

More Posts