| ContextIDInterceptor.java |
/*
* $Id: ContextIDInterceptor.java,v 1.2 2012/02/27 22:26:03 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.server;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.NDC;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
import org.xins.common.manageable.BootstrapException;
import org.xins.common.manageable.InitializationException;
import org.xins.common.text.TextUtils;
/**
* Interceptor for diagnostic context identifiers.
* The key is generated by the ContextIDGenerator and set on the NDC by the ContextIDInterceptor.
*
* @see ContextIDGenerator
* @since XINS 3.0
*
* @version $Revision: 1.2 $ $Date: 2012/02/27 22:26:03 $
* @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
*/
public class ContextIDInterceptor extends Interceptor {
private ContextIDGenerator contextIdGenerator;
/**
* Pattern which incoming diagnostic context identifiers must match. Can be
* <code>null</code> in case no pattern has been specified. Initially this
* field is indeed <code>null</code>.
*/
private Pattern contextIDPattern;
private boolean applyContextID;
@Override
protected void bootstrapImpl(Map<String, String> properties)
throws MissingRequiredPropertyException,
InvalidPropertyValueException,
BootstrapException {
contextIdGenerator = new ContextIDGenerator(getApi().getName());
contextIdGenerator.bootstrap(properties);
}
@Override
protected void initImpl(Map<String, String> properties)
throws MissingRequiredPropertyException,
InvalidPropertyValueException,
InitializationException {
// Determine filter for incoming diagnostic context IDs
contextIDPattern = determineContextIDPattern(properties);
contextIdGenerator.init(properties);
applyContextID = !"false".equals(properties.get(ConfigManager.CONTEXT_ID_PUSH_PROPERTY));
}
@Override
public HttpServletRequest beginRequest(HttpServletRequest httpRequest) {
if (applyContextID) {
applyContextID(httpRequest);
}
return httpRequest;
}
@Override
public void endRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
if (applyContextID) {
NDC.pop();
NDC.remove();
}
}
/**
* Applies an applicable diagnostic context identifier.
* If no diagnostic context identifier is specified or if the value is
* invalid, a new one is created and applied.
*
* @param request
* the HTTP servlet request, should not be <code>null</code>.
*
* @return
* the diagnostic context identifier, never <code>null</code> and never
* an empty string.
*/
private String applyContextID(HttpServletRequest request) {
// See if the request already specifies a diagnostic context identifier
// XXX: Store "_context" in a constant
// Associate the current diagnostic context identifier with this thread
String contextID = request.getParameter("_context");
if (TextUtils.isEmpty(contextID)) {
contextID = contextIdGenerator.generate();
NDC.push(contextID);
Log.log_3583(contextID);
// Indeed there is a context ID in the request, make sure it's valid
} else {
// Valid context ID
if (isValidContextID(contextID)) {
NDC.push(contextID);
Log.log_3581(contextID);
// Invalid context ID
} else {
Log.log_3582(contextID);
contextID = contextIdGenerator.generate();
NDC.push(contextID);
Log.log_3583(contextID);
}
}
return contextID;
}
/**
* Determines if the specified incoming context identifier is considered
* valid.
*
* @param contextID
* the incoming diagnostic context identifier, should not be
* <code>null</code>.
*
* @return
* <code>true</code> if <code>contextID</code> is considered acceptable,
* <code>false</code> if it is considered unacceptable.
*/
private boolean isValidContextID(String contextID) {
// If a filter is specified, validate that the ID matches it
if (contextIDPattern != null) {
return contextIDPattern.matcher(contextID).matches();
// No filter is specified, everything is allowed
} else {
return true;
}
}
/**
* Determines the filter for diagnostic context identifiers.
*
* @param properties
* the runtime properties to retrieve information from, cannot be
* <code>null</code>.
*
* @return
* the filter as a {@link Pattern} object, or <code>null</code> if no
* filter is specified.
*
* @throws IllegalArgumentException
* if <code>properties == null</code>.
*
* @throws InvalidPropertyValueException
* if the value for the filter property is considered invalid.
*/
private Pattern determineContextIDPattern(Map<String, String> properties)
throws IllegalArgumentException, InvalidPropertyValueException {
// Check preconditions
MandatoryArgumentChecker.check("properties", properties);
// Determine pattern string
// XXX: Store "org.xins.server.contextID.filter" in a constant
String propName = "org.xins.server.contextID.filter";
String propValue = properties.get(propName);
// If the property value is empty, then there is no pattern
Pattern pattern;
if (TextUtils.isEmpty(propValue)) {
pattern = null;
Log.log_3431();
// Otherwise we must provide a Pattern instance
} else {
// Convert the string to a Pattern
try {
// XXX: Why is the pattern made case-insensitive?
pattern = Pattern.compile(propValue, Pattern.CASE_INSENSITIVE);
Log.log_3432(propValue);
// Malformed pattern indicates an invalid value
} catch (PatternSyntaxException exception) {
Log.log_3433(propValue);
InvalidPropertyValueException ipve;
ipve = new InvalidPropertyValueException(propName, propValue);
ipve.initCause(exception);
throw ipve;
}
}
return pattern;
}
}