Northwind TagLocationService

From RifidiWiki

Revision as of 17:14, 3 June 2010 by Gustavo.cabral (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This is Step 8 in the Northwind Application Tutorial
Previous Step: Step 7: Write a Hello World Servlet
Next Step: Northwind Display Events using JSP: Step 9: Display events in a JSP

What you will learn

  • How to use a service to manage events
  • How to receive and process events over JMS

Now that we have all the MVC plumbing working, we need to hook up the RFID application with the web application. We will do this by creating a service that the controller can use to find out the list of tags at each stage. Then we will write a JMS listener who will update the service with the latest information. Finally we will inject the service into the contoller.

Write a Service Interface

  • Create a new package called com.northwind.rfid.shipping.war.service
  • Create a new Interface called TagLocationService
package com.northwind.rfid.shipping.war.service;

import java.util.List;
import com.northwind.rfid.shipping.notifications.AlertNotification;

/**
 * This service provides the current list of tags at the Dock Door and the Weigh
 * Station, as well as all the alerts that have happened so far
 * 
 * @author Kyle Neumeier - kyle@pramari.com
 * 
 */
public interface TagLocationService {
	/**
	 * Get a list of all packages at the dock door
	 * 
	 * @return
	 */
	List<String> getDockDoorItems();

	/**
	 * Get a list of all packages at the Weigh Station
	 * 
	 * @return
	 */
	List<String> getWeighStationItems();

	/**
	 * Get a list of all the alerts
	 * 
	 * @return
	 */
	List<AlertNotification> getAlerts();
}

Write a Manager Interface

The TagLocationService interface allows a component to consume information. We also need the ability to add data to the service as it arrives. As such, let's create a second interface with the required methods.

  • Create a new package called com.northwind.rfid.shipping.war.service.impl
  • Create a new Interface called TagLocationServiceManager
package com.northwind.rfid.shipping.war.service.impl;

import com.northwind.rfid.shipping.notifications.AlertNotification;

/**
 * The manager of the TagLocationService. It allows a component to add data.
 * 
 * @author Kyle Neumeier - kyle@pramari.com
 * 
 */
public interface TagLocationServiceManager {

	/**
	 * Called when a new package has arrived at the dockdoor
	 * 
	 * @param item
	 */
	void PackageArrivedAtDockDoor(String item);

	/**
	 * Called when a package departs from the dock door
	 * 
	 * @param item
	 */
	void PackageDepartedFromDockDoor(String item);

	/**
	 * Called when an item arrives at the weigh station
	 * 
	 * @param item
	 */
	void PackageArrivedAtWeighStation(String item);

	/**
	 * Called when an item departs from the weigh station
	 * 
	 * @param item
	 */
	void PackageDepartedFromWeighStation(String item);

	/**
	 * Called when a new alert happens
	 * 
	 * @param notification
	 */
	void Alert(AlertNotification notification);
}

Implement the Service

Create a class called TagLocationServiceImpl in the com.northwind.rfid.shipping.war.service.impl package. There are a few considerations to take into account:

  1. The class needs to be thread-safe, since multiple threads might access the class. In our case, there are two: The JMS thread will call the manager methods, while the servlet thread will call the service methods. In order to prevent any race conditions, I wrapped the three data structures in synchronized collections. I also made sure to copy the collections before I returned them.
  2. We probably want to preserve the ordering that tags were inserted into the lists, however we don't want to store one tag more than once in each data structure. Therefore, I used a LinkedHashSet.
package com.northwind.rfid.shipping.war.service.impl;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.northwind.rfid.shipping.notifications.AlertNotification;
import com.northwind.rfid.shipping.war.service.TagLocationService;

/**
 * Thread-safe implementation of TagLocationService and TagLocationServiceManager
 * interfaces.
 * 
 * @author Kyle Neumeier - kyle@pramari.com
 * 
 */
public class TagLocationServiceImpl implements TagLocationService,
		TagLocationServiceManager {

	/** All the items that can be seen at dock door */
	private final Set<String> dockDoor = Collections
			.synchronizedSet(new LinkedHashSet<String>());
	/** All the items that can be seen at the weigh station */
	private final Set<String> weighstation = Collections
			.synchronizedSet(new LinkedHashSet<String>());
	/** All the alerts that have been created */
	private final List<AlertNotification> alerts = Collections
			.synchronizedList(new LinkedList<AlertNotification>());

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.TagLocationService#getAlerts()
	 */
	@Override
	public List<AlertNotification> getAlerts() {
		return new LinkedList<AlertNotification>(alerts);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.TagLocationService#getDockDoorItems
	 * ()
	 */
	@Override
	public List<String> getDockDoorItems() {
		return new LinkedList<String>(dockDoor);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.northwind.rfid.shipping.war.service.TagLocationService#
	 * getWeighStationItems()
	 */
	@Override
	public List<String> getWeighStationItems() {
		return new LinkedList<String>(weighstation);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager
	 * #Alert(com.northwind.rfid.shipping.notifications.AlertNotification)
	 */
	@Override
	public void Alert(AlertNotification notification) {
		this.alerts.add(notification);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager
	 * #PackageArrivedAtDockDoor(java.lang.String)
	 */
	@Override
	public void PackageArrivedAtDockDoor(String item) {
		this.dockDoor.add(item);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager
	 * #PackageArrivedAtWeighStation(java.lang.String)
	 */
	@Override
	public void PackageArrivedAtWeighStation(String item) {
		this.weighstation.add(item);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager
	 * #PackageDepartedFromDockDoor(java.lang.String)
	 */
	@Override
	public void PackageDepartedFromDockDoor(String item) {
		this.dockDoor.remove(item);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager
	 * #PackageDepartedFromWeighStation(java.lang.String)
	 */
	@Override
	public void PackageDepartedFromWeighStation(String item) {
		this.weighstation.remove(item);
	}
}

Write a JMS Listener

Create a new class called MessageReceiver in the com.northwind.rfid.shipping.war package.

package com.northwind.rfid.shipping.war;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

import com.northwind.rfid.shipping.notifications.AlertNotification;
import com.northwind.rfid.shipping.notifications.EZone;
import com.northwind.rfid.shipping.notifications.ItemArrivalNotification;
import com.northwind.rfid.shipping.notifications.ItemDepartureNotification;
import com.northwind.rfid.shipping.war.service.impl.TagLocationServiceManager;

/**
 * The class that implements the logic of receiving a notification from JMS
 * using the TagLocationServiceManager to update the TagLocationService
 * 
 * @author Kyle Neumeier - kyle@pramari.com
 * 
 */
public class MessageReceiver implements MessageListener {

	/** The TagLocationServiceManager to use */
	private volatile TagLocationServiceManager TLSManager;

	/**
	 * Called by spring
	 * 
	 * @param TLSManager
	 *            The TagLocationServiceManager
	 */
	public void setTLSManager(TagLocationServiceManager TLSManager) {
		this.TLSManager = TLSManager;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
	 */
	@Override
	public void onMessage(Message arg0) {
		if (arg0 instanceof BytesMessage) {
			try {
				// deserialize the message
				Object message = deserialize((BytesMessage) arg0);
				if (message instanceof ItemArrivalNotification) {
					// If the message is an ItemArrival
					ItemArrivalNotification notification = (ItemArrivalNotification) message;
					if (notification.getZone() == EZone.DOCK_DOOR) {
						// If it arrived at the dock door
						TLSManager.PackageArrivedAtDockDoor(notification
								.getTag_Id());
					} else {
						// otherwise it arrived at the weigh station
						TLSManager.PackageArrivedAtWeighStation(notification
								.getTag_Id());
					}
				} else if (message instanceof ItemDepartureNotification) {
					// if the message is an Item Departure
					ItemDepartureNotification notification = (ItemDepartureNotification) message;
					if (notification.getZone() == EZone.DOCK_DOOR) {
						// If it departed from the dock door
						TLSManager.PackageDepartedFromDockDoor(notification
								.getTag_Id());
					} else {
						// otherwise it departed from the weigh station
						TLSManager.PackageDepartedFromWeighStation(notification
								.getTag_Id());
					}
				} else if (message instanceof AlertNotification) {
					// if the message is an alert
					TLSManager.Alert((AlertNotification) message);
				} else {
					System.out.println("Notification type not recognized");
				}
			} catch (JMSException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	/***
	 * A private helper method that deserializes the incoming JMS notifications
	 * 
	 * @param message
	 *            The message to deserialize
	 * @return
	 * @throws JMSException
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	private Object deserialize(BytesMessage message) throws JMSException,
			IOException, ClassNotFoundException {
		int length = (int) message.getBodyLength();
		byte[] bytes = new byte[length];
		message.readBytes(bytes);
		ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(
				bytes));
		return in.readObject();
	}
}