Logging Jersey Requests and Responses in Mule

By | July 26, 2015

The carpentry-season is not quite over yet, but I miss blogging so here is a little something written during a couple of rainy days.

In this article I will show how to add logging of requests and responses to/from a RESTful webservice that uses Jersey and runs in a Mule application.
As a side-effect and since I want to show a complete example program, I will also show how to create Mule projects using the Mule application Maven archetype and the basics of developing a Jersey RESTful web service in Mule.
In this article I will use the Mule 3.7.0 Community Edition, Jersey 2.11 and REST-assured 2.4.1, to mention the more prominent frameworks.

Configure Maven

Before being able to use the Mule application archetype you need to add the Mule repositories to your Maven settings file (settings.xml). One of the ways this is accomplished is described below.

  • Add the following profile containing the Mule repositories inside the <profiles> element:

  •  Add the following XML element inside the <pluginGroups> element:

Create a Mule Project

Since I have abandoned Eclipse and AnypointStudio in favour for IntelliJ IDEA, I will use the Mule Maven archetype to create the example project. Eclipse and NetBeans are also able to import Maven projects so this is really the most universal way to create a Mule application project regardless of which IDE you use.

  • Open a terminal window.
  • Go to the directory in which you want the example project to be created.
  • Create the example project using the following command:

  • In order for the proper JDK to be assigned to the project, edit the pom.xml file in the new project and add the following plug-in to the <build> section:

  •  In the <dependencies> section, add the following test dependency to REST-assured, which will be used in the example project to test the RESTful webservice:

The project is now ready to be imported into the IDE of choice.

Log4J Configuration Files

Logging is in the title of this article, so we will need a Log4J2 configuration file for the Mule application. The reason for using Log4J2 is, for those not aware of this, that it is the logging framework of choice for the Mule ESB.

  • In src/main/resources, create a file named log4j2.xml with the following contents:

  • In src/test/resources, create a file named log4j2-test.xml with the same contents as the log4j2.xml file created above.

Note that:

  • There is a logger with the name attribute set to “se.ivankrizsan.mule.jerseylogging.IvansJerseyLoggingFilter”.
    Setting its level to “debug” will enable logging from the Jersey logger class we will implement later. Setting its level to “info” or higher will disable logging.

Entity Class

The REST service implemented in this example will allow for managing of customers. Our service thus need a Customer entity class.

  • Create the Customer class in src/main/java, in the package declared below:

Note that:

  • The class is annotated with @XmlRootElement.
    This is in order for JAXB to be able to create an XML representation of the entity.
  • There are no annotations related to creating a JSON representation of the entity.
    Jackson, which is the library responsible to create JSON representations, does not need any annotations to create JSON representations. You can use Jackson annotations to customize creation of JSON representations, but I leave that for a later article.

REST Resource Test Class

In order to be able to conveniently assess our progress we implement a test of the REST resource class that will be implemented later. The test class only contains one single test, but again this article is about logging requests and responses to/from REST web services implemented using Jersey and one test which sends data to and receives data from the service is enough in order for us to be able to view the results of the logging.

  • In src/test/java, in the appropriate package, implement the class CustomerResourceTest as below:

Note that:

  • The test is a standard Mule test-case, inheriting from the FunctionalTestCase class.
  • In the setUp method, REST-assured is initialized.
    REST-assured is a very nice library that makes testing REST web services easy.
  • In the testCreateNewCustomer method the JSON representation of a new customer is sent to the service and the result is verified to be HTTP status 200 and containing JSON data.
  • The call to the post method in the testCreateNewCustomer method uses a constant in the CustomerResource class.
    Since we haven’t created the CustomerResource class yet, there will be a compilation error.

REST Resource Class

The customer entity has an accompanying resource class that exposes the RESTful service.

  • Implement the CustomerResource class in src/main/java like this:

Note that:

  • The @Path annotation specify the relative path at which the resource is to be made available.
  • The @Produces annotation specify which representation types the resource should be able to produce.
    In this case I want to be able to retrieve both XML and JSON representations of customers.
  • The customers repository is a hashtable.
    A hashtable has been used to simplify the example but is of course not advisable for real REST resources.
  • There is a random number generator stored in the instance variable mCustomerIdGenerator.
    The random number generator is used to generate ids when creating new customers in the customer repository – something which the database will take care of when using a real repository.
  • In the constructor a customer is created and inserted into the repository.
    Again, this is not something you would find in a normal REST resource implementation class but something I put there to have some data in the repository for the sake of the example.
  • In the getAllCustomers method, an array of customers are placed in the response object.
    In its standard configuration, JAXB doesn’t quite like Java lists and is not able to serialize these into XML.
  • The remaining methods implement the basic CRUD functionality.
    I have chosen to use POST to create customers and PUT to update customers.

Mule Configuration File

A Mule configuration file named mule-config.xml is already present in the project at src/main/app.

  • Open the mule-config.xml file and replace its contents with the following XML data:

Note that:

  • There is a <http:listener-config> element in which a host and a port is specified.
    This is similar to a Mule transport’s connector, but for the new Mule HTTP transport. The host is declared as 0.0.0.0, in order to be available to clients outside of the computer on which the service is run.
  • There is a Spring bean named customerResource declared.
    While it is not necessary to declare Spring beans for REST resources, I find it convenient if there are properties that need to be injected into the resource instances.
    If you do not want to create Spring beans for the REST resources, use the class attribute on the <component> element like in this example: <component class=”se.ivankrizsan.mule.jerseylogging.resources.CustomersResource”/>
  • The first element in the <flow> is a <http:listener> element.
    This element exposes an endpoint using the http listener configuration declared earlier at the path specified by the path attribute. Using the value “resources/v100/*” for the path results in the customer resource developed earlier (having the path “/customers”) will be exposed at http://0.0.0.0:8083/resources/v100/customers, where 0.0.0.0 will need to be replaced with the external IP address of the computer running the service if the client is not within the same computer.
  • For each REST resource that is to be exposed, one <component> element needs to be created inside the <jersey:resources> element.
  • There is a <jersey:package> element which specify the package “se.ivankrizsan.mule.jerseylogging”.
    This is how we tell Jersey where to look for classes that modify and/or extend Jersey when the Jersey webservices are deployed in the Mule ESB.
    Later in this article we will see an example of how to implement such a class.

We should now be able to run the CustomerResourceTest implemented earlier and the test should pass.

Logging with the Jersey LoggingFilter

In Jersey there are filters and interceptors that can be inserted into the path of requests and responses. Filters allow for access to entire requests and responses, while interceptors are more focused on the body of the message; be it an object or a stream. More information about Jersey filters and interceptors can be found here. Since we want to log all properties of requests and responses, this article will focus solely on Jersey filters.
The developers of Jersey does indeed provide a filter for logging to be found in the LoggingFilter class. Before we develop our own logging filter, lets try the Jersey logging filter..

  • In src/main/java, implement the OriginalJerseyLoggingFilter class:

As you can see this class contains nothing, it just extends the Jersey LoggingFilter class. However, note the @Provider annotation on the class.
The reason for creating this class and annotating it with @Provider is in order for the filter to be discovered by Jersey when we start Mule. Remember the configuration line in the mule-config.xml file that tells Jersey to automatically discover extension classes:

If we now run the CustomerResourceTest, there should be output similar to this in the console:

All but the last line, the line that has the message “Stopping flow: restServiceFlow”, are output from the Jersey LoggingFilter. If we examine the LoggingFilter class, we can see that it uses the Java default logging. In addition we can also note that all the methods that are not found in any of the interfaces the LoggingFilter class implements are private. This makes it difficult, if not impossible, to extend this class.

Creating a Logging Jersey Filter from Scratch

In order to have all the logging from my Mule application that uses Jersey write to the same log file, I set out to implement my own Jersey logging filter. In addition, I wanted to create something with a slightly higher degree of extensibility. As of writing this article, this filter is only for the server-side, since it implements the ContainerRequestFilter and ContainerResponseFilter interfaces.
If you want a logging filter for the client-side as well, implement the ClientRequestFilter and ClientResponseFilter interfaces too.

  • In src/main/java, implement the class IvansJerseyLoggingFilter as follows:

Note that:

  • If logging is not enabled, both the filter methods will immediately return without processing or changing anything.
  • The filter method that is invoked upon a request being received is the filter(ContainerRequestContext) method.
    This method will log some information about requests, such as which HTTP method was used, which content-type the message has and the HTTP headers. In addition, the message body, up to a certain size determined by the mMaxBodySize instance variable, will also be logged.
  • The other filter method that takes two context object parameters is the one that will be invoked to log responses.
    This method will log the HTTP status, the content-type as well as the HTTP headers of responses.
  • The methods isLoggingEnabled and logMessage are protected.
    This enables subclasses to replace the Log4J logging with some other logging framework or mechanism.

If we once again run the CustomerResourceTest, the new logging filter will produce output similar to this:

Again, the last line “Stopping flow: restServiceFlow” is not output from the logging filter.
This concludes this article and it is back to carpentry for me. The sourcecode for the example project is available on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *