header

JMS Logger with ActiveMQ

The main beneffit to log our application with JMS is to use asynchronous logging, meaning services creates some log messages, sends them across the network to a JMS provider and then the receiver store that message in our log system.

First we need to set our logger service structure

package com.jos.dem.jmailer.service

interface LoggerService {
  void notifyRequest(def requestParams)
}

Logger service implementation

package com.jos.dem.jmailer.service.impl

import javax.jms.Message
import javax.jms.ObjectMessage
import javax.jms.Session
import javax.jms.Destination
import javax.jms.JMSException

import org.springframework.jms.core.JmsTemplate
import org.springframework.jms.core.MessageCreator
import org.springframework.stereotype.Service
import org.springframework.beans.factory.annotation.Autowired

import com.jos.dem.jmailer.service.LoggerService

import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory

@Service
class LoggerServiceImpl implements LoggerService {

  @Autowired
  JmsTemplate template

  @Autowired
  Destination logger

  Log log = LogFactory.getLog(getClass())

  void notifyRequest(requestParams) {
    log.info "CALLING Notifying"

      template.send(logger, new MessageCreator() {
        public Message createMessage(Session session) throws JMSException {
          ObjectMessage message = session.createObjectMessage()
          message.setObject(requestParams)
          message
        }
      })
  }

}

In our logger service implementation we create an message and using an JMS template we deliver our message to the destination.

The receiver should be an MessageListener implementation

package com.jos.dem.jmailer.messengine

import javax.jms.Message
import javax.jms.MessageListener
import javax.jms.ObjectMessage

import org.springframework.stereotype.Service
import org.springframework.beans.factory.annotation.Autowired

import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory

@Service
class LoggerMessageListener implements MessageListener {

  Log log = LogFactory.getLog(getClass())

  def void onMessage(Message message) {
    def data =  ((ObjectMessage) message).getObject()
    String dataToString = data.collect { k, v -> v }.join(';')
    log.info dataToString
  }

}

In this class we receive our message, and using collect() method we iterate over message and transform each element in an string delimited by ;

Now, the idea to use this structure is to catch all request from our client from the controller using an interceptor as follow:

package com.jos.dem.jmailer.interceptor

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.servlet.HandlerInterceptor
import org.springframework.web.servlet.ModelAndView

import com.jos.dem.jmailer.service.LoggerService

class LoggerInterceptor implements HandlerInterceptor {

  @Autowired
  LoggerService loggerService

  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    def data = [:]
    data.remoteHost = request.remoteHost
    data.timeInMillis = System.currentTimeMillis()
    data.method = request.method
    data.requestURL = request.requestURL
    data.parameters = request.parameterMap
    loggerService.notifyRequest(data)
    return true
  }


  void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
  }


  void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
  }

}

We need to define our interceptor in the dispatcher servlet.

File: src/main/webapp/WEB-INF/dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/mvc
  http://www.springframework.org/schema/mvc/spring-mvc.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd ">

  <import resource="classpath:/aop-appctx.xml" />
  <import resource="classpath:/jms-appctx.xml" />
  <context:component-scan base-package="com.jos.dem.jmailer" />

  <mvc:interceptors>
    <bean class="com.jos.dem.jmailer.interceptor.LoggerInterceptor" />
  </mvc:interceptors>

  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/views/jsp/" />
    <property name="suffix" value=".jsp" />
  </bean>

  <mvc:resources mapping="/resources/**" location="/resources/" />

  <mvc:annotation-driven />

</beans>

Finally we need to declare our jms context, and don’t forget to import it to the dispatcher servler.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:jms="http://www.springframework.org/schema/jms"
  xmlns:amq="http://activemq.apache.org/schema/core"
  xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd
  http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

  <!--  Embedded ActiveMQ Broker -->
  <amq:broker id="broker" useJmx="false" persistent="false">
    <amq:transportConnectors>
      <amq:transportConnector uri="tcp://localhost:0" />
    </amq:transportConnectors>
  </amq:broker>

  <!--  ActiveMQ Destination  -->
  <amq:queue id="logger" physicalName="loggerMessageListener" />

  <!-- JMS ConnectionFactory to use, configuring the embedded broker using XML -->
  <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost" />

  <!-- JMS Producer Configuration -->
  <bean id="jmsProducerConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    depends-on="broker"
    p:targetConnectionFactory-ref="jmsFactory" />

  <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
    p:connectionFactory-ref="jmsProducerConnectionFactory" />

  <!-- JMS Consumer Configuration -->
  <bean id="jmsConsumerConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    depends-on="broker"
    p:targetConnectionFactory-ref="jmsFactory" />

  <jms:listener-container container-type="default"
    connection-factory="jmsConsumerConnectionFactory"
    acknowledge="auto">
    <jms:listener destination="loggerMessageListener" ref="loggerMessageListener" />
  </jms:listener-container>

</beans>

To download the project

git clone https://github.com/josdem/jmailer-bootstrap.git
git fetch
git checkout feature/jms-logger

Return to the main article

comments powered by Disqus