Per-thread data can be useful. Here's how to do it in the context of servlet code because servlet code requires a more precise approach.
In terms of beginning Java samples you have:
Here's the per-thread data we want to keep for use in issuing richer statements to the log.
package com.acm.web.user.thread; import java.util.Date; /** * Thread-safe collection of state such that each thread will get its own instance * of everything below, almost only for the purpose of logging, without any of this * being trampled by other threads executing the same classes and code paths. * * Clean-up * * This data must be formally abandoned via the remove() method below at the end * of the thread's work because the executing thread comes from a pool maintained * by Tomcat. */ public class ThreadContext { // general status... private String ipaddress; private boolean ping; // our app is being ping'd // request/executing status... private String httpmethod; private String httpuri; private String httpbaseuri; private String httpqueryparms; private Date requesttime; // response status... private Date responsetime; private int httpstatus; public String getIpaddress() { return this.ipaddress; } public void setIpaddress( String ipaddress ) { this.ipaddress = ipaddress; } public boolean isPing() { return this.ping; } public void setPing( boolean ping ) { this.ping = ping; } public String getHttpmethod() { return this.httpmethod; } public void setHttpmethod( String httpmethod ) { this.httpmethod = httpmethod; } public String getHttpuri() { return this.httpuri; } public void setHttpuri( String httpuri ) { this.httpuri = httpuri; } public String getHttpbaseuri() { return this.httpbaseuri; } public void setHttpbaseuri( String httpbaseuri ) { this.httpbaseuri = httpbaseuri; } public String getHttpqueryparms() { return this.httpqueryparms; } public void setHttpqueryparms( String httpqueryparms ) { this.httpqueryparms = httpqueryparms; } public Date getRequesttime() { return requesttime; } public void setRequesttime( Date requesttime ) { this.requesttime = requesttime; } public Date getResponsetime() { return responsetime; } public void setResponsetime( Date responsetime ) { this.responsetime = responsetime; } public int getHttpstatus() { return httpstatus; } public void setHttpstatus( int httpstatus ) { this.httpstatus = httpstatus; } // ----------------------------------------------------------------------------------------------- // Above this line is our per-thread data that we want accessible at any random point in our code. // Below are the mechanics of implementing per-thread data in Java. // ----------------------------------------------------------------------------------------------- private static final ThreadLocal< ThreadContext > context = new ThreadLocal< ThreadContext >() { @Override protected ThreadContext initialValue() { ThreadContext context = new ThreadContext(); context.ping = false; context.requesttime = new Date(); return context; } }; public static ThreadContext get() { return context.get(); } public static void remove() { context.remove(); } /** * This is more for use by the Eclipse debugger than anything else. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append( "{\n" ); if( this.ipaddress != null ) sb.append( " ipaddress: " + this.ipaddress + "\n" ); sb.append( " ping: " + this.ping + "\n" ); if( this.httpmethod != null ) sb.append( " httpmethod: " + this.httpmethod + "\n" ); if( this.httpuri != null ) sb.append( " httpuri: " + this.httpuri + "\n" ); if( this.httpbaseuri != null ) sb.append( " httpbaseuri: " + this.httpbaseuri + "\n" ); if( this.httpqueryparms != null ) sb.append( " httpqueryparms: " + this.httpqueryparms + "\n" ); if( this.requesttime != null ) sb.append( " requesttime: " + this.requesttime.toString() + "\n" ); if( this.responsetime != null ) sb.append( " responsetime: " + this.responsetime.toString() + "\n" ); sb.append( " httpstatus: " + this.httpstatus + "\n" ); sb.append( "}" ); return sb.toString(); } }
In the code below, especially for the tear-down code, I think the comments say it all.
package com.acme.web.filter; import java.net.URI; import org.apache.log4j.Logger; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; import com.acme.web.user.thread.ThreadContext; public class ContextCreationFilter implements ResourceFilter, ContainerRequestFilter { private static Logger log = Logger.getLogger( ContextCreationFilter.class ); @Context HttpServletRequest httpRequest; public ContextCreationFilter() { log.info( "ContextCreationFilter is installed" ); } /** * Set up per-thread context for gathering state that will be used to issue * statements to the log. */ @Override public ContainerRequest filter( ContainerRequest request ) { ThreadContext ctx = ThreadContext.get(); // get our thread-local store... URI uri = request.getRequestUri(); ctx.setIpaddress( httpRequest.getRemoteAddr() ); ctx.setHttpmethod( request.getMethod() ); ctx.setHttpuri( request.getRequestUri().toASCIIString() ); ctx.setHttpbaseuri( request.getBaseUri().getPath() ); ctx.setHttpqueryparms( uri.getQuery() ); String ping = request.getPath(); if( StringUtil.isEmptyOrNullLiteral( ping ) ) ctx.setPing( true ); return request; } @Override public ContainerRequestFilter getRequestFilter() { return this; } @Override public ContainerResponseFilter getResponseFilter() { return null; } }
package com.acme.web.filter; import org.apache.log4j.Logger; import com.acme.web.user.thread.ThreadContext; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponse; import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; public class ContextTearDownFilter implements ResourceFilter, ContainerResponseFilter { private static Logger log = Logger.getLogger( ContextTearDownFilter.class ); public ContextTearDownFilter() { log.info( "ContextTearDownFilter is installed" ); } /** * Tear down per-thread context here (see ContextCreationFilter) because * Tomcat likely will not clear it off our thread before putting it back into its * pool. This would otherwise result in a memory leak. */ @Override public ContainerResponse filter( ContainerRequest request, ContainerResponse response ) { ThreadContext.remove(); return response; } @Override public ContainerRequestFilter getRequestFilter() { return null; } @Override public ContainerResponseFilter getResponseFilter() { return this; } }
<servlet> <servlet-name> Jersey REST Service </servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name> com.sun.jersey.config.property.packages </param-name> <param-value> com.acme.web.user.service </param-value> </init-param> <!-- Do not disturb the order of these two request filters! --> <init-param> <param-name> com.sun.jersey.spi.container.ContainerRequestFilters </param-name> <param-value> com.acme.web.filter.ContextCreationFilter,com.acme.web.filter.HeaderDetectionFilter </param-value> </init-param> <init-param> <param-name> com.sun.jersey.spi.container.ContainerResponseFilters </param-name> <param-value> com.acme.web.filter.ContextTearDownFilter </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet>
Above, HeaderDetectionFilter is registered only to show how two or more of these filters are specified (the mechanism might not have been obvious if you've never done this before).
Devoid of allegiance to servlet issues, here's a short example in straight JSE.
package example; import java.text.SimpleDateFormat; /** * Thread-safe implementation of SimpleDateFormat each thread will get its own * instance of SimpleDateFormat and a thread name that will not be shared with * other threads. * * What's happening? * * A new instance of ThreadLocal is created, colored with Context. This instance * records, via the overridden initialValue() method, an instance of Context that * happens to be initialized, if not permanently so, to some determined state. * To be useful, it will be necessary to allow that state to be modified all * during execution (and not merely, as demonstrated by this sample code, read * for its initial values). */ public class Context { private String threadName; private SimpleDateFormat dateFormat; public String getThreadName() { return threadName; } public void setThreadName( String threadName ) { this.threadName = threadName; } public SimpleDateFormat getDateFormat() { return dateFormat; } public void setDateFormat( SimpleDateFormat dateFormat ) { this.dateFormat = dateFormat; } // --------------------------------------------------------------------------------------------------- // Above this line was our per-thread data that we want accessible at any random point in our code. // Below are the mechanics of implementing per-thread data in Java. See Javadoc in previous examples. // --------------------------------------------------------------------------------------------------- private static final ThreadLocal< Context > context = new ThreadLocal< Context >() { @Override protected Context initialValue() { return new Context(); } }; public static Context get() { return context.get(); } public static void remove() { context.remove(); } }
package example; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; /** * Create two threads, each with its own data. Methods return elements (pieces) * of that per-thread (thread-safe) data. * * Output * * Setting thread name : Thread-1 * Setting thread name : Thread-0 * Creating SimpleDateFormat for Thread : Thread-1 * Creating SimpleDateFormat for Thread : Thread-0 * Thread: Thread-1 Formatted Date: 05/02/2013 17:51:00 * Thread: Thread-0 Formatted Date: 05/02/2013 17:51:00 * Thread: Thread-1 Formatted Date: 05/02/2013 17:51:00 * Thread: Thread-0 Formatted Date: 05/02/2013 17:51:00 */ public class ExampleMain { public static void main( String args[] ) throws IOException { Thread t1 = new Thread( new TlTask() ); Thread t2 = new Thread( new TlTask() ); t1.start(); t2.start(); } } /** * This represents the executing code of an application making use of per-thread * data. */ class TlTask implements Runnable { @Override public void run() { Context ctx = Context.get(); // get our thread-local store... // these statements represent establishing (non-initial) values in our thread-local store... ctx.setThreadName( Thread.currentThread().getName() ); ctx.setDateFormat( new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" ) ); // these statements represent obtaining values from our thread-local store... String threadName = ctx.getThreadName(); SimpleDateFormat fmt = ctx.getDateFormat(); System.out.println( "Setting thread name : " + threadName ); System.out.println( "Creating SimpleDateFormat for Thread : " + threadName ); for( int i = 0; i < 2; i++ ) { Date date = new Date(); String formatted = fmt.format( date ); System.out.println( "Thread: " + threadName + " Formatted Date: " + formatted ); } Context.remove(); // abandon our thread-local store to garbage collection... } }