Kontakt

Mapped Diagnostic Context with Tapestry Filters

Posted on Dienstag, 29th Dezember, 2009

In this post Howard describes how you can handle security in your Tapestry app. The approach is based on Tapestry’s request processing mechanism that allows you to extend the framework by your own contributions. In his post Howard contributes a filter for the ComponentRequestHandler pipeline to prevent any access to particular pages unless the user is logged in. For more details read Howard’s post.

Based on Howard’s post I would like to show you how to differentiate the logging output of one client from another. In web applications multiple clients request the same pages, components and services simultaneously. This fact leads to tons of logging output that is unusable because you can’t see which logging output belongs to which user. A simple technique called Mapped Diagnostic Contexts (MDC) can help you to stamp each logging output. The idea is to put contextual information into your logger and is described here.

Mapped Diagnostic Context is a map which is managed on a per thread basis. In log4j or slf4j you can put key-value pairs into the context. These pairs will be used by the logging framework when the logging output is generated. Let’s have a look at a simple log4j configuration. The most interesting part is %X{username}. The %X conversion specifier tells log4j that the contextual information username should be put into the logging output. When generating output the logger will look for a value the context under this key. If available the value will apear in the output.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration>
   <appender name="console">
      <param name="Threshold" value="INFO"/>
      <layout>
         <param name="ConversionPattern"
            value="%d{dd.MM.yyyy HH:mm:ss,SSS} [%X{username}] # %-5p # %c # [%C{1}.%M] # %m%n"/>
      </layout>
   </appender>

   <root>
      <appender-ref ref="console"/>
   </root>

</log4j:configuration>

Now let’s put the name of the logged in user into the MDC. A ComponentRequestFilter is a part of the Tapestry’s request processing mechanism and that’s why a good place to provide the contextual information.  The following filter along with a bunch of other filters is responsible for handling the page requests or component events. Every time a page is rendered or a component event is triggered our MDCFilter will put the name of the logged in user into the context before delegating to the next handler in the pipeline. After the invocation of the next handler is finished the name of the user is removed from the context.

public class MDCFilter implements ComponentRequestFilter {

    static final String MDC_KEY = "username";
    private final ApplicationStateManager applicationStateManager;

    public MDCFilter(final ApplicationStateManager manager) {
        this.applicationStateManager = manager;
    }

    public void handleComponentEvent(
	    ComponentEventRequestParameters parameters,
	    ComponentRequestHandler handler) throws IOException {
        put();

        try {
            handler.handleComponentEvent(parameters);
        } finally {
            remove();
        }
    }

    public void handlePageRender(
	    PageRenderRequestParameters parameters,
	    ComponentRequestHandler handler) throws IOException {
        put();

        try {
            handler.handlePageRender(parameters);
        } finally {
            remove();
        }

    }

    private void put() {
        User user = this.applicationStateManager
                            .getIfExists(User.class);

        if (user != null) {
            MDC.put(MDC_KEY, user.getUsername());
        }
    }

    private void remove() {
        MDC.remove(MDC_KEY);
    }

}

The last part of this is to plugin into the request processing mechanism of Tapestry. This is done by a contribution to the ComponentRequestHandler service’s configuration.

public class AppModule {
   public static void contributeComponentRequestHandler(
         OrderedConfiguration configuration) {
      configuration.addInstance("MDC", MDCFilter.class);
   }
}

If you are using Howard’s ComponentRequestFilter to check if the user is logged in you should place the MDCFilter after the RequiresLoginFilter. For this purpose we pass the order constraint after:RequiresLogin into the third parameter of the addInstance() method. This constraint is used to order the object relative to other contributed objects. This way MDCFilter will be executed after the RequiresLoginFilter.

public class AppModule {
   public static void contributeComponentRequestHandler(
         OrderedConfiguration configuration) {
      configuration.addInstance(
            "MDC", MDCFilter.class, "after:RequiresLogin"");
   }
}

That’s it. When a user is logged in you will see logging output like the following one. If the user is not there you will see just [] instead of [bart.simpson].

28.12.2009 22:55:41,066 [bart.simpson] # INFO  # foo.bar.pages.MyPage # [MyPage.onSuccess] # Bla bla bla

It is worth to mention that ComponentRequestHandler pipeline is not the only one place to plug into the request processing mechanism. You can also contribute a RequestFilter to the RequestHandler pipeline. Check out this diagramm for more details.

 

 

avatar

Your name

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 

Tapestry 5 Blog - Copyright © 2009 - Eclectic Theme by Your Inspiration Web - Powered by WordPress