blob: 874be5352981a978184f7d195437a2340e29d2ea [file] [log] [blame]
/*
* Copyright 2005-2008 Les Hazlewood
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jsecurity.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.subject.Subject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
/**
* A ThreadContext provides a means of binding and unbinding objects to the
* current thread based on key/value pairs.
*
* <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs
* for each thread.</p>
*
* <p>If the desired behavior is to ensure that bound data is not shared across
* threads in a pooled or reusable threaded environment, the application (or more likely a framework) must
* bind and remove any necessary values at the beginning and end of stack
* execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p>
*
* @see #clear()
*
* @since 0.1
* @author Les Hazlewood
*/
@SuppressWarnings( value = { "unchecked", "unsafe" } )
public abstract class ThreadContext {
protected static transient final Log logger = LogFactory.getLog( ThreadContext.class );
public static final String SUBJECT_KEY = Subject.class.getName() + "_THREAD_CONTEXT_KEY";
public static final String INET_ADDRESS_KEY = InetAddress.class.getName() + "_JSECURITY_THREAD_CONTEXT_KEY";
public static final String SERVLET_REQUEST_KEY = ServletRequest.class.getName() + "_JSECURITY_THREAD_CONTEXST_KEY";
public static final String SERVLET_RESPONSE_KEY = ServletResponse.class.getName() + "_JSECURITY_THREAD_CONTEXT_KEY";
protected static ThreadLocal<Map<Object, Object>> resources =
new InheritableThreadLocal<Map<Object, Object>>() {
protected Map<Object, Object> initialValue() {
return new HashMap<Object, Object>();
}
};
protected ThreadContext() {
}
/**
* Returns the ThreadLocal Map. This Map is used internally to bind objects
* to the current thread by storing each object under a unique key.
*
* @return the map of bound resources
*/
protected static Map<Object, Object> getResources() {
return resources.get();
}
/**
* Returns the object for the specified <code>key</code> that is bound to
* the current thread.
*
* @param key the key that identifies the value to return
* @return the object keyed by <code>key</code> or <code>null</code> if
* no value exists for the specified <code>key</code>
*/
public static Object get( Object key ) {
if ( logger.isTraceEnabled() ) {
String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
logger.trace( msg );
}
Object value = getResources().get( key );
if ( ( value != null ) && logger.isTraceEnabled() ) {
String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
logger.trace( msg );
}
return value;
}
/**
* Binds <tt>value</tt> for the given <code>key</code> to the current thread.
*
* <p>A <tt>null</tt> <tt>value</tt> has the same effect as if <tt>remove</tt> was called for the given
* <tt>key</tt>, i.e.:
*
* <pre>
* if ( value == null ) {
* remove( key );
* }</pre>
*
* @param key The key with which to identify the <code>value</code>.
* @param value The value to bind to the thread.
* @throws IllegalArgumentException if the <code>key</code> argument is <tt>null</tt>.
*/
public static void put( Object key, Object value ) {
if ( key == null ) {
throw new IllegalArgumentException( "key cannot be null" );
}
if ( value == null ) {
remove( key );
return;
}
getResources().put( key, value );
if ( logger.isTraceEnabled() ) {
String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
logger.trace( msg );
}
}
/**
* Unbinds the value for the given <code>key</code> from the current
* thread.
*
* @param key The key identifying the value bound to the current thread.
* @return the object unbound or <tt>null</tt> if there was nothing bound
* under the specified <tt>key</tt> name.
*/
public static Object remove( Object key ) {
Object value = getResources().remove( key );
if ( ( value != null ) && logger.isTraceEnabled() ) {
String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" +
key + "]" + "from thread [" + Thread.currentThread().getName() + "]";
logger.trace( msg );
}
return value;
}
/**
* Returns true if a value for the <code>key</code> is bound to the current thread, false otherwise.
*
* @param key the key that may identify a value bound to the current thread.
* @return true if a value for the key is bound to the current thread, false
* otherwise.
*/
public static boolean containsKey( Object key ) {
return getResources().containsKey( key );
}
/**
* Removes <em>all</em> values bound to this ThreadContext, which includes any Subject, Session, or InetAddress
* that may be bound by these respective objects' conveninece methods, as well as all values bound by your
* application code.
*
* <p>This operation is meant as a clean-up operation that may be called at the end of
* thread execution to prevent data corruption in a pooled thread environment.
*/
public static void clear() {
getResources().clear();
if ( logger.isTraceEnabled() ) {
logger.trace( "Removed all ThreadContext values from thread [" + Thread.currentThread().getName() + "]" );
}
}
/**
* Convenience method that simplifies retrieval of a thread-bound Subject. If there is no
* Subject bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper
* for the following:
* <pre>
* return (Subject)get( SECURITY_CONTEXT_KEY );</pre>
*
* <p>This method only returns the bound value if it exists - it does not remove it
* from the thread. To remove it, one must call {@link #unbindSubject() unbindSubject()} instead.
*
* @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
* @since 0.2
*/
public static Subject getSubject() {
return (Subject)get(SUBJECT_KEY);
}
/**
* Convenience method that simplifies binding a Subject to the ThreadContext.
*
* <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
* ThreadContext key names. The implementation is simple in that, if the Subject is not <tt>null</tt>,
* it binds it to the thread, i.e.:
*
* <pre>
* if (subject != null) {
* put( SECURITY_CONTEXT_KEY, subject );
* }</pre>
*
* @param subject the Subject object to bind to the thread. If the argument is null, nothing will be done.
* @since 0.2
*/
public static void bind( Subject subject) {
if ( subject != null ) {
put(SUBJECT_KEY, subject);
}
}
/**
* Convenience method that simplifies removal of a thread-local Subject from the thread.
*
* <p>The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
* merely a conveient wrapper for the following:
*
* <pre>
* return (Subject)remove( SECURITY_CONTEXT_KEY );</pre>
*
* <p>If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
* thread execution), you should use the {@link #getSubject() getSubject()} method for that purpose.
*
* @return the Subject object previously bound to the thread, or <tt>null</tt> if there was none bound.
* @since 0.2
*/
public static Subject unbindSubject() {
return (Subject)remove(SUBJECT_KEY);
}
/**
* Convenience method that simplifies retrieval of a thread-bound InetAddress. If there is no
* InetAddress bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper
* for the following:
* <pre>
* return (InetAddress)get( INET_ADDRESS_KEY );</pre>
*
* <p>This method only returns the bound value if it exists - it does not remove it
* from the thread. To remove it, one must call {@link #unbindInetAddress() unbindInetAddress} instead.
*
* @return the InetAddress object bound to the thread, or <tt>null</tt> if there isn't one bound.
* @since 0.2
*/
public static InetAddress getInetAddress() {
return (InetAddress)get( INET_ADDRESS_KEY );
}
/**
* Convenience method that simplifies binding an InetAddress to the ThreadContext.
*
* <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
* ThreadContext key names. The implementation is simple in that, if the inetAddress is not <tt>null</tt>,
* it binds it to the thread, i.e.:
*
* <pre>
* if (inetAddress != null) {
* put( INET_ADDRESS_KEY, inetAddress );
* }</pre>
*
* @param inetAddress the InetAddress to bind to the thread. If the argument is null, nothing will be done.
* @since 0.2
*/
public static void bind( InetAddress inetAddress ) {
if ( inetAddress != null ) {
put( INET_ADDRESS_KEY, inetAddress );
}
}
/**
* Convenience method that simplifies removal of a thread-local InetAddress from the thread.
*
* <p>The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
* merely a conveient wrapper for the following:
*
* <pre>
* return (InetAddress)remove( INET_ADDRESS_KEY );</pre>
*
* <p>If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
* thread execution), you should use the {@link #getInetAddress() getInetAddress()} method for that purpose.
*
* @return the InetAddress object previously bound to the thread, or <tt>null</tt> if there was none bound.
* @since 0.2
*/
public static InetAddress unbindInetAddress() {
return (InetAddress)remove( INET_ADDRESS_KEY );
}
/**
* Convenience method that simplifies retrieval of a thread-bound ServletRequest. If there is no
* ServletRequest bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper
* for the following:
* <pre>
* return (ServletRequest)get( SERVLET_REQUEST_KEY );</pre>
*
* <p>This method only returns the bound value if it exists - it does not remove it
* from the thread. To remove it, one must call {@link #unbindServletRequest() unbindServletRequest} instead.
*
* @return the ServletRequest bound to the thread, or <tt>null</tt> if there isn't one bound.
* @since 0.2
*/
public static ServletRequest getServletRequest() {
return (ServletRequest)get( SERVLET_REQUEST_KEY );
}
/**
* Convenience method that simplifies binding a ServletRequest to the ThreadContext.
*
* <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
* ThreadContext key names. The implementation is simple in that, if the servletRequest is not <tt>null</tt>,
* it binds it to the thread, i.e.:
*
* <pre>
* if (servletRequest != null) {
* put( SERVLET_REQUEST_KEY, session );
* }</pre>
*
* @param servletRequest the ServletRequest object to bind to the thread. If the argument is null, nothing will be done.
* @since 0.2
*/
public static void bind( ServletRequest servletRequest ) {
if ( servletRequest != null ) {
put( SERVLET_REQUEST_KEY, servletRequest );
}
}
/**
* Convenience method that simplifies removal of a thread-local ServletRequest from the thread.
*
* <p>The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
* merely a conveient wrapper for the following:
*
* <pre>
* return (ServletRequest)remove( SERVLET_REQUEST_KEY );</pre>
*
* <p>If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
* thread execution), you should use the {@link #getServletRequest() getServletRequest()} method for that purpose.
*
* @return the Session object previously bound to the thread, or <tt>null</tt> if there was none bound.
* @since 0.2
*/
public static ServletRequest unbindServletRequest() {
return (ServletRequest)remove( SERVLET_REQUEST_KEY );
}
/**
* Convenience method that simplifies retrieval of a thread-bound ServletResponse. If there is no
* ServletResponse bound to the thread, this method returns <tt>null</tt>. It is merely a convenient wrapper
* for the following:
* <pre>
* return (ServletResponse)get( SERVLET_RESPONSE_KEY );</pre>
*
* <p>This method only returns the bound value if it exists - it does not remove it
* from the thread. To remove it, one must call {@link #unbindServletResponse() unbindServletResponse} instead.
*
* @return the ServletResponse bound to the thread, or <tt>null</tt> if there isn't one bound.
* @since 0.2
*/
public static ServletResponse getServletResponse() {
return (ServletResponse)get( SERVLET_RESPONSE_KEY );
}
/**
* Convenience method that simplifies binding a ServletResponse to the ThreadContext.
*
* <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
* ThreadContext key names. The implementation is simple in that, if the servletResponse is not <tt>null</tt>,
* it binds it to the thread, i.e.:
*
* <pre>
* if (servletResponse != null) {
* put( SERVLET_RESPONSE_KEY, session );
* }</pre>
*
* @param servletResponse the ServletResponse object to bind to the thread. If the argument is null, nothing will be done.
* @since 0.2
*/
public static void bind( ServletResponse servletResponse ) {
if ( servletResponse != null ) {
put( SERVLET_RESPONSE_KEY, servletResponse );
}
}
/**
* Convenience method that simplifies removal of a thread-local ServletResponse from the thread.
*
* <p>The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
* merely a conveient wrapper for the following:
*
* <pre>
* return (ServletResponse)remove( SERVLET_RESPONSE_KEY );</pre>
*
* <p>If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
* thread execution), you should use the {@link #getServletResponse() getServletResponse()} method for that purpose.
*
* @return the Session object previously bound to the thread, or <tt>null</tt> if there was none bound.
* @since 0.2
*/
public static ServletResponse unbindServletResponse() {
return (ServletResponse)remove( SERVLET_RESPONSE_KEY );
}
}