Review
In the previous section, we created the core Event management system and utilized the Spring Data JPA project to simplify data access. We've also added AJAX functionality to make the application responsive. In this section, we will integrate RabbitMQ and messaging in general.Where am I?
Table of Contents
Messaging Support
Our application is capable of broadcasting events as simple text messages. These messages are CRUD actions and exceptions that arise during the lifetime of the application. This means if a user performs an add, edit, delete, or get these actions will be published to a message broker. If there are any exceptions, we will also publish them.Why do we need to publish these events? First, it helps us study and explore RabbitMQ messaging. Second, we can have a third-party application whose sole purpose is to handle these messages for intensive statistical analysis. Third, it allows us to monitor in real-time and check the status of our application instantaneously.
If you're unfamiliar with messaging and its benefits, I suggest visiting the following links:
We'll divide this page into four sections:
- B. Messaging Support
- B1. Aspect
- B2. Configuration
- B3. Controller
- B4. View
B1. Aspect
We will publish CRUD operations as simple messages. It's like logging except we send the logs to a message broker. Usually we would add this feature across existing classes. Unfortunately, this leads to "scattering" and " tangling" of code. No doubt--it's a crosscutting concern. We have the same scenario when added the logging feature earlier (See A5. Crosscutting Concerns section earlier).We have created an Aspect, EventRabbitAspect.java, to solve this concern. To send messages, we use the AmqpTemplate where we pass the exchange, routing key, and the message. Since we want to publish messages as they are called and also errors as they happened, we use @Around to capture these events. The proceeding code is a variation of the original tutorial Chatting in the Cloud: Part 1 by Mark Fisher (http://blog.springsource.com/2011/08/16/chatting-in-the-cloud-part-1/)
EventRabbitAspect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.krams.tutorial.aop; | |
import java.util.Date; | |
import org.apache.log4j.Logger; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import org.springframework.amqp.core.AmqpTemplate; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.core.annotation.Order; | |
import org.springframework.stereotype.Component; | |
/** | |
* Interceptor for publishing messages to RabbitMQ | |
* | |
* @author krams at {@link http://krams915@blogspot.com} | |
*/ | |
@Aspect | |
@Order(1) | |
@Component | |
public class EventRabbitAspect { | |
protected Logger logger = Logger.getLogger("aop"); | |
@Autowired | |
private volatile AmqpTemplate amqpTemplate; | |
public static final String RABBIT_EXCHANGE = "eventExchange"; | |
public static final String GENERAL_EVENT_ROUTE_KEY = "event.general.*"; | |
public static final String ERROR_EVENT_ROUTE_KEY = "event.error.*"; | |
@Around("execution(* org.krams.tutorial.service.EventService.*(..))") | |
public Object interceptService(ProceedingJoinPoint pjp) throws Throwable { | |
try { | |
logger.debug("Publishing event to RabbitMQ"); | |
this.amqpTemplate.convertAndSend(RABBIT_EXCHANGE, GENERAL_EVENT_ROUTE_KEY, new Date() + ": " + pjp.toShortString()); | |
return pjp.proceed(); | |
} catch (Exception e) { | |
logger.debug("Publishing event to RabbitMQ"); | |
this.amqpTemplate.convertAndSend(RABBIT_EXCHANGE, ERROR_EVENT_ROUTE_KEY, new Date() + ": " + pjp.getSignature().toLongString() + " - " + e.toString()); | |
return pjp.proceed(); | |
} | |
} | |
} |
In order for Spring to recognize this aspect, make sure to declare the following element in your XML configuration (we've done this in the trace-context.xml):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- For parsing classes with @Aspect annotation --> | |
<aop:aspectj-autoproxy/> |
B2. Configuration
After declaring the Aspect, we now declare and configure the RabbitMQ and Spring AMQP specific settings. In fact, you will soon discover that most of the work with Spring AMQP is configuration-related: declaration of queues, bindings, exchanges, and connection settings.We've declared two queues: eventQueue for normal events, and errorQueue for errors. We have a single exchange, eventExchange where we declare the bindings for the two queues.
In order to send messages to the eventQueue, we set the routing key to event.general.*. Likewise, to send messages to the errorQueue, we set the routing key to event.error.*. For a tutorial on these concepts, please visit the official examples at http://www.rabbitmq.com/getstarted.html
spring-rabbit.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beans xmlns="http://www.springframework.org/schema/beans" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:p="http://www.springframework.org/schema/p" | |
xmlns:context="http://www.springframework.org/schema/context" | |
xmlns:rabbit="http://www.springframework.org/schema/rabbit" | |
xmlns:cloud="http://schema.cloudfoundry.org/spring" | |
xsi:schemaLocation=" | |
http://www.springframework.org/schema/beans | |
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd | |
http://www.springframework.org/schema/context | |
http://www.springframework.org/schema/context/spring-context-3.1.xsd | |
http://www.springframework.org/schema/rabbit | |
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd | |
http://schema.cloudfoundry.org/spring | |
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd"> | |
<context:property-placeholder location="/WEB-INF/spring.properties" /> | |
<rabbit:queue id="eventQueue"/> | |
<rabbit:queue id="errorQueue"/> | |
<rabbit:topic-exchange name="eventExchange"> | |
<rabbit:bindings> | |
<rabbit:binding queue="eventQueue" pattern="event.general.*"/> | |
<rabbit:binding queue="errorQueue" pattern="event.error.*"/> | |
</rabbit:bindings> | |
</rabbit:topic-exchange> | |
<rabbit:template connection-factory="rabbitConnectionFactory" exchange="eventExchange"/> | |
<rabbit:admin connection-factory="rabbitConnectionFactory"/> | |
<rabbit:listener-container> | |
<rabbit:listener queues="eventQueue" ref="monitorController" method="handleEvent"/> | |
<rabbit:listener queues="errorQueue" ref="monitorController" method="handleError"/> | |
</rabbit:listener-container> | |
<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory" | |
p:username="guest" p:password="guest" p:port="5672"> | |
<constructor-arg value="localhost" /> | |
</bean> | |
<!-- Cloud-based | |
<cloud:rabbit-connection-factory id="rabbitConnectionFactory"/> --> | |
</beans> |
B3. Controller
After configuring RabbitMQ and Spring AMQP, we declare a controller, aptly named MonitorController. It's main purpose is to handle monitoring requests. This controller is based on Chatting in the Cloud: Part 1 blog by Mark Fisher (http://blog.springsource.com/2011/08/16/chatting-in-the-cloud-part-1/).MonitorController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.krams.tutorial.controller; | |
import java.util.Queue; | |
import java.util.concurrent.LinkedBlockingQueue; | |
import org.springframework.amqp.core.AmqpTemplate; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.util.StringUtils; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
@Controller | |
@RequestMapping("/monitor") | |
public class MonitorController { | |
@Autowired | |
private volatile AmqpTemplate amqpTemplate; | |
private final Queue<String> eventMessages = new LinkedBlockingQueue<String>(); | |
private final Queue<String> errorMessages = new LinkedBlockingQueue<String>(); | |
@RequestMapping(value = "/event") | |
public String eventPage() { | |
return "event-monitor-page"; | |
} | |
@RequestMapping(value = "/error") | |
public String errorPage() { | |
return "error-monitor-page"; | |
} | |
@RequestMapping(value = "/event/log") | |
@ResponseBody | |
public String eventLog() { | |
return StringUtils.arrayToDelimitedString(this.eventMessages.toArray(), "<br/>"); | |
} | |
@RequestMapping(value = "/error/log") | |
@ResponseBody | |
public String errorLog() { | |
return StringUtils.arrayToDelimitedString(this.errorMessages.toArray(), "<br/>"); | |
} | |
/** | |
* Handles normal event messages. | |
* This method is invoked when a RabbitMQ message is received. | |
*/ | |
public void handleEvent(String message) { | |
if (eventMessages.size() > 100) { | |
eventMessages.remove(); | |
} | |
eventMessages.add(message); | |
} | |
/** | |
* Handles error messages. | |
* This method is invoked when a RabbitMQ message is received. | |
*/ | |
public void handleError(String message) { | |
if (errorMessages.size() > 100) { | |
errorMessages.remove(); | |
} | |
errorMessages.add(message); | |
} | |
} |
B4. View
We have two JSP pages event-monitor-page.jsp and error-monitor-page.jsp for event and error views respectively. We've utilized AJAX to pull information from the application. Again, this is based on Chatting in the Cloud: Part 1 blog by Mark Fisher (http://blog.springsource.com/2011/08/16/chatting-in-the-cloud-part-1/).event-monitor-page.jsp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> | |
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> | |
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %> | |
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> | |
<%@ page session="false" %> | |
<c:url value="/" var="rootUrl"/> | |
<c:url value="/resources" var="resourcesUrl"/> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<!-- CSS Imports--> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/jquery/dark-hive/jquery-ui-1.8.6.custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/main/main.css"/> | |
<!-- JS Imports --> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-1.5.2.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-ui-1.8.12.custom.min.js"></script> | |
<title>Event Monitor</title> | |
</head> | |
<body class="ui-widget-content"> | |
<div id="menu"> | |
<ul> | |
<li><a href="${rootUrl}event">Events (DataTables)</a></li> | |
<li><a href="${rootUrl}jqgrid/event">Events (jQgrid)</a></li> | |
<li><a href="${rootUrl}error">Errors</a></li> | |
<li><a href="${rootUrl}monitor/event">Monitor Events</a></li> | |
<li><a href="${rootUrl}monitor/error">Monitor Errors</a></li> | |
</ul> | |
<br style="clear:left"/> | |
</div> | |
<script type="text/javascript"> | |
var running = false; | |
var timer; | |
function load() { | |
if (running) { | |
$.ajax({ | |
url : '${rootUrl}monitor/event/log', | |
success : function(message) { | |
if (message && message.length) { | |
var messagesDiv = $('#log'); | |
messagesDiv.html(message); | |
messagesDiv.animate({ scrollTop: messagesDiv.attr("scrollHeight") - messagesDiv.height() }, 150); | |
} | |
timer = poll(); | |
}, | |
error : function() { | |
timer = poll(); | |
}, | |
cache : false | |
}); | |
} | |
} | |
function start() { | |
if (!running) { | |
running = true; | |
if (timer != null) { | |
clearTimeout(timer); | |
} | |
timer = poll(); | |
} | |
} | |
function poll() { | |
if (timer != null) { | |
clearTimeout(timer); | |
} | |
return setTimeout(load, 1000); | |
} | |
$(function() { | |
start(); | |
}); | |
</script> | |
<h3 class="title">Event Monitor</h3> | |
<div id="log" class="monitor"> </div> | |
</body> | |
</html> |
error-monitor-page.jsp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> | |
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> | |
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %> | |
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> | |
<%@ page session="false" %> | |
<c:url value="/" var="rootUrl"/> | |
<c:url value="/resources" var="resourcesUrl"/> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<!-- CSS Imports--> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/jquery/dark-hive/jquery-ui-1.8.6.custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/main/main.css"/> | |
<!-- JS Imports --> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-1.5.2.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-ui-1.8.12.custom.min.js"></script> | |
<title>Error Monitor</title> | |
</head> | |
<body class="ui-widget-content"> | |
<div id="menu"> | |
<ul> | |
<li><a href="${rootUrl}event">Events (DataTables)</a></li> | |
<li><a href="${rootUrl}jqgrid/event">Events (jQgrid)</a></li> | |
<li><a href="${rootUrl}error">Errors</a></li> | |
<li><a href="${rootUrl}monitor/event">Monitor Events</a></li> | |
<li><a href="${rootUrl}monitor/error">Monitor Errors</a></li> | |
</ul> | |
<br style="clear:left"/> | |
</div> | |
<script type="text/javascript"> | |
var running = false; | |
var timer; | |
function load() { | |
if (running) { | |
$.ajax({ | |
url : '${rootUrl}monitor/error/log', | |
success : function(message) { | |
if (message && message.length) { | |
var messagesDiv = $('#log'); | |
messagesDiv.html(message); | |
messagesDiv.animate({ scrollTop: messagesDiv.attr("scrollHeight") - messagesDiv.height() }, 150); | |
} | |
timer = poll(); | |
}, | |
error : function() { | |
timer = poll(); | |
}, | |
cache : false | |
}); | |
} | |
} | |
function start() { | |
if (!running) { | |
running = true; | |
if (timer != null) { | |
clearTimeout(timer); | |
} | |
timer = poll(); | |
} | |
} | |
function poll() { | |
if (timer != null) { | |
clearTimeout(timer); | |
} | |
return setTimeout(load, 1000); | |
} | |
$(function() { | |
start(); | |
}); | |
</script> | |
<h3 class="title">Error Monitor</h3> | |
<div id="log" class="monitor"> </div> | |
</body> | |
</html> |
Result
You can preview the final output by visiting our live app at http://spring-mysql-mongo-rabbit.cloudfoundry.com/monitor/eventNext Section
In the next section, we will integrate MongoDB and add error persistence to the current application. To proceed to the next section, click here.
Share the joy:
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |


Hi there! Do you have the new URLs for the demo in the pivotal world? :-)
ReplyDeleteI have read your blog its very attractive and impressive. I like it your blog.
ReplyDeleteSpring online training Spring online training Spring Hibernate online training Spring Hibernate online training Java online training
spring training in chennai spring hibernate training in chennai