Generic way to log the response times of your REST resources

With RESTeasy it’s not that easy to log the response times of your resources in a generic way. The framework provides you several interceptors that you can use. And implementing two of them you can easily log your response times quite nice:

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PostProcessInterceptor;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Provider
@ServerInterceptor
public class ResponseTimeLogger implements PreProcessInterceptor, PostProcessInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(ResponseTimeLogger.class);

    public static final String REQUEST_START_TIME = "RequestStartTime";

    @Context
    private HttpServletRequest servletRequest;

    public ServerResponse preProcess(HttpRequest request, ResourceMethod resourceMethod) throws Failure, WebApplicationException {
        servletRequest.setAttribute(REQUEST_START_TIME, System.currentTimeMillis());
        return null;
    }

    @Override
    public void postProcess(ServerResponse serverResponse) {
        try {
            logger.info("Invoked resource {}.{}. ip={}, uri={}, status={}, duration={}ms,",
                    serverResponse.getResourceClass().getName(),
                    serverResponse.getResourceMethod().getName(),
                    servletRequest.getRemoteAddr(),
                    servletRequest.getRequestURI(),
                    serverResponse.getStatus(),
                    (System.currentTimeMillis() - ((Long) servletRequest.getAttribute(REQUEST_START_TIME)))
            );
        } catch (RuntimeException e) {
            logger.warn("Error while logging request data.", e);
        }
    }
}

 

Start Message Driven Bean as a ‘singleton’

Sometimes you have the need to start only one MessageDrivenBean (MDB) to process the messages of a queue. One reason could be that the order of the message matters. And if you would process them with more than one instance the order you process them is not guaranteed. Per default the MDB are linked with a bean pool of 20 instances.

But you cannot annotate a MDB with @Singleton, so you have to create a specific bean pool and associate your MDB with this pool.

First of all, add the needed annotations to your project:


         <dependency>
            <groupId>org.jboss.ejb3</groupId>
            <artifactId>jboss-ejb3-ext-api</artifactId>
            <version>2.2.0.Final</version>
        </dependency>

After add an additional instance pool to our JBoss:

Via CLI:

/subsystem=ejb3/strict-max-bean-instance-pool=single-instance-pool:add(max-pool-size=1,timeout=5,timeout-unit=MINUTES)

Or simply add the following lines to your standalone-full.xml


             <pools>
                <bean-instance-pools>
                    <strict-max-pool name="slsb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
                    <strict-max-pool name="mdb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
                    <strict-max-pool name="single-instance-pool" max-pool-size="1" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
                </bean-instance-pools>
            </pools>

And as last step, link your MDB with the created pool:


@Pool("single-instance-pool")
@MessageDriven(
        activationConfig = {
                @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/queue/ExpiryQueue")
        },
        mappedName = "jms/queue/ExpiryQueue")
public class SingletonMdb implements MessageListener {

    private static final Logger logger = getLogger(SingletonMdb.class);

    @Override
    public void onMessage(Message message) {
        TextMessage tm = (TextMessage) message;

        try {
            String text = tm.getText();
            logger.info("Message received: {}", text);
        } catch (JMSException e) {
            logger.error("Error occured: ", e);
        }

    }


    @PostConstruct
    public void postConstruct() {
        logger.info("instance created {}", this.toString());
    }
}

 

You find a complete example on github: https://github.com/erard22/singleton-mdb-example

Just clone and execute the following steps:

  • Start your JBoss with:
  1. For Linux:   JBOSS_HOME/bin/standalone.sh -c standalone-full.xml
    For Windows: JBOSS_HOME\bin\standalone.bat -c standalone-full.xml
  • Configure the new bean pool:
  • mvn clean package wildfly:execute-commands
  • Deploy the application:
  • mvn clean package wildfly:execute-commands wildfly:deploy
  • Send Messages to the queue, by just calling the url:
  • http://localhost:8080/singleton-mdb-example/rest/message/test

Doing it several times you will se in the console that the bean is only created once even if there would be concurrent requests:

22:08:46,138 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-38 (HornetQ-client-global-threads-2080930476)) instance created ch.erard22.ee.messaging.mdb.SingletonMdb@1d9c36c9
22:08:46,138 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-38 (HornetQ-client-global-threads-2080930476)) Message received: test
22:08:51,719 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-45 (HornetQ-client-global-threads-2080930476)) Message received: test
22:08:53,554 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-44 (HornetQ-client-global-threads-2080930476)) Message received: test
22:08:54,891 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-45 (HornetQ-client-global-threads-2080930476)) Message received: test
22:08:55,930 INFO  [ch.erard22.ee.messaging.mdb.SingletonMdb] (Thread-44 (HornetQ-client-global-threads-2080930476)) Message received: test

Sonarlint

As I’m an IntelliJ user I normally don’t have the need anymore to browse for plugins, but this one is really nice:

http://www.sonarlint.org/

SonarLint is an extension to your favorite IDE that provides on-the-fly feedback to developers on new bugs and quality issues injected into their code.