blob: d655e0ce8d962c209df43de84cb1220e3a9cf351 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.myfaces.trinidadinternal.config;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.portlet.faces.annotation.ExcludeFromManagedRequestScope;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import org.apache.myfaces.trinidad.config.Configurator;
import org.apache.myfaces.trinidad.context.ExternalContextDecorator;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.context.RequestContextFactory;
import org.apache.myfaces.trinidad.context.WindowManager;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.skin.SkinFactory;
import org.apache.myfaces.trinidad.skin.SkinProvider;
import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
import org.apache.myfaces.trinidad.util.ComponentReference;
import org.apache.myfaces.trinidad.util.ExternalContextUtils;
import org.apache.myfaces.trinidad.util.RequestStateMap;
import org.apache.myfaces.trinidad.util.RequestType;
import org.apache.myfaces.trinidadinternal.context.RequestContextFactoryImpl;
import org.apache.myfaces.trinidadinternal.context.external.ServletCookieMap;
import org.apache.myfaces.trinidadinternal.context.external.ServletRequestHeaderMap;
import org.apache.myfaces.trinidadinternal.context.external.ServletRequestHeaderValuesMap;
import org.apache.myfaces.trinidadinternal.context.external.ServletRequestMap;
import org.apache.myfaces.trinidadinternal.context.external.ServletRequestParameterMap;
import org.apache.myfaces.trinidadinternal.context.external.ServletRequestParameterValuesMap;
import org.apache.myfaces.trinidadinternal.skin.SkinFactoryImpl;
import org.apache.myfaces.trinidadinternal.skin.provider.ExternalSkinProvider;
import org.apache.myfaces.trinidadinternal.skin.provider.SkinProviderRegistry;
import org.apache.myfaces.trinidadinternal.skin.provider.TrinidadSkinProvider;
/**
* This is the implementation of the Trinidad's Global configurator. It provides the entry point for
* all other configurators. This class is responsible for enforcing the contract outlined by the
* Configurator abstract class, but allows a more "relaxed" implementation of the APIs called for by
* the Configurator class, making it more convenient to use ConfiguratorServices from within the
* Trinidad renderkit. Where appropriate, these differences will be documented for the benifit of
* the Trindad developer.
*
* @see org.apache.myfaces.trinidad.config.Configurator
* @version $Revision: daschnei_20131204_er17813713/3 $ $Date: 2014/01/21 13:53:38 $
*/
public final class GlobalConfiguratorImpl
extends Configurator
{
/**
* Returns a GlobalConfigurator instance for the current context's class loader. The
* GlobalConfigurator is responsible for enforcing the contract on the other methods of this
* class. This means that if {@link #init(ExternalContext)} is called multiple times, the global
* configurator will call all subordinate configurators only once.
*
* Likewise, the GlobalConfigurator will return exceptions when the contract is expressly violated
* (like if {@link #getExternalContext(ExternalContext)} is called before a {{@link #beginRequest(ExternalContext)}.
*
* @return a GlobalConfigurator or <code>null</code> is one was unable to be obtained.
*/
static public final void releaseInstance()
{
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null)
{
_LOG.severe("CANNOT_FIND_CONTEXT_CLASS_LOADER");
return;
}
synchronized (_CONFIGURATORS)
{
GlobalConfiguratorImpl config = _CONFIGURATORS.remove(loader);
_LOG.fine("GlobalConfigurator has been removed.");
}
}
static public final GlobalConfiguratorImpl getInstance()
{
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null)
{
_LOG.severe("CANNOT_FIND_CONTEXT_CLASS_LOADER");
}
else
{
synchronized (_CONFIGURATORS)
{
GlobalConfiguratorImpl config = _CONFIGURATORS.get(loader);
if (config == null)
{
try
{
config = new GlobalConfiguratorImpl();
_CONFIGURATORS.put(loader, config);
}
catch (final RuntimeException e)
{
// OC4J was not reporting these errors properly:
_LOG.severe(e);
throw e;
}
_LOG.fine("GlobalConfigurator has been created.");
}
return config;
}
}
return null;
}
/**
* Returns true if the request has not been stated for the current "virtual"
* request. In the servlet environment this will be true after
* {@link #beginRequest(ExternalContext)} is executed and before
* {@link #endRequest(ExternalContext)} is executed. This will generally
* happen once per request. In the Portlet Environment, the request must be
* be started and ended at the beginning and end of both the actionRequest
* and the RenderRequest.
*
* @param ec
* @return
*/
static public boolean isRequestStarted(ExternalContext ec)
{
return (RequestStateMap.getInstance(ec).get(_REQUEST_TYPE) != null);
}
/**
* Returns "true" if the services should be considered enabled or disabled.
*
* @param ec
* @return
*/
static private final boolean _isDisabled(final ExternalContext ec)
{
final Boolean inRequest = (Boolean) RequestStateMap.getInstance(ec).get(_IN_REQUEST);
if (inRequest == null)
{
return isConfiguratorServiceDisabled(ec);
}
else
{
final boolean disabled = inRequest.booleanValue();
if (disabled != isConfiguratorServiceDisabled(ec))
{
_LOG.warning("Configurator services were disabled after beginRequest was executed. Cannot disable configurator services");
}
return disabled;
}
}
/**
* Private default constructor. Right now this class is not serializable. If serialization is
* required, we may wish to make this public. We really don't want people using this though.
*/
private GlobalConfiguratorImpl()
{
}
/**
* Executes the beginRequest methods of all of the configurator services. This method will also
* initizlize the configurator if it has not already been initialized, so there may be no need to
* call the {@link #init(ExternalContext)} method directly.
*
* This method also ensures that the requestContext is attached before the beginRequest methods
* are called, so there is no reason to initialize the request context before calling this method.
* In portal environments, it is important to execute this method once for each Portlet action and
* render request so that the requestContext may be properly initialized even though the
* underlying services will be called only once per physical request.
*
* @param ec the externalContext to use to begin the request.
*
* @see Configurator#beginRequest(javax.faces.context.ExternalContext)
*/
@SuppressWarnings("unchecked") // TODO: remove this for Faces 1.2
@Override
public void beginRequest(ExternalContext ec)
{
// asserts for debug which disappear in production
assert ec != null;
RequestStateMap state = RequestStateMap.getInstance(ec);
RequestType requestType = (RequestType) state.get(_REQUEST_TYPE);
// Do per-virtual request stuff
if (requestType == null)
{
// RequestType may change in a portal environment. Make sure it's set up to enforce the
// contracts
requestType = ExternalContextUtils.getRequestType(ec);
RequestStateMap.getInstance(ec).put(_REQUEST_TYPE, requestType);
// By contract, Configurators beginRequest is only called once per physical request.
// The globalConfigurator may be called multiple times, however, so we need to enforce
// the contract.
if (!_isDisabled(ec))
{
// If this hasn't been initialized then please initialize
if (!_initialized.get())
{
init(ec);
}
_attachRequestContext(ec);
if (state.get(_IN_REQUEST) == null)
{
// Allow WindowManager to perform some processing at the beginning of request and optionally complete the response
if (!Boolean.TRUE.equals(state.get(_CONFIGURATORS_ABORTED)))
{
if (_beginWindowManagerRequest(ec))
{
_startConfiguratorServiceRequest(ec);
}
else
{
FacesContext.getCurrentInstance().responseComplete();
state.put(_CONFIGURATORS_ABORTED, Boolean.TRUE);
}
}
}
}
else
{
_LOG.fine("GlobalConfigurator: Configurators have been disabled for this request.");
}
}
else if (!requestType.equals(ExternalContextUtils.getRequestType(ec)))
{
// This will happen if the actionRequest was not ended before dispatching to the render
// request
throw new IllegalStateException("The previous action request was not ended.");
}
else
{
_LOG.fine("BeginRequest called multiple times for this request");
}
}
/**
* Cleans up the current configurator. This will execute the destroy method on all of the
* configurator services. Generally this will be called by Trinidad's context listener when the
* context is destroyed, but it may be used manually to allow the configurator to be
* re-initialized.
*
* Calling this method while the configurator is not initialized will not re-execute the destroy
* methods on the services.
*
* @see org.apache.myfaces.trinidad.config.Configurator#destroy()
*/
@Override
public void destroy()
{
if (_initialized.get())
{
try
{
//Forces atomic operations with init. If we are in the middle of an init or another destroy, we'll
//wait on this lock until our operations are complete. We then have to recheck our initialized state.
_initLock.lock();
if (_initialized.get())
{
for (final Configurator config: _services)
{
try
{
config.destroy();
}
catch (final Throwable t)
{
// we always want to continue to destroy things, so log errors and continue
_LOG.severe(t);
}
}
_services = null;
_initialized.set(false);
}
}
finally
{
//release any managed threadlocals that may have been used durring destroy
_releaseManagedThreadLocals();
_initLock.unlock();
}
}
}
/**
* Ends the currently begun request. It is important to note that this should be executed only
* once per physical request.
*
* @see org.apache.myfaces.trinidad.config.Configurator#endRequest(javax.faces.context.ExternalContext)
*/
@Override
public void endRequest(ExternalContext ec)
{
RequestStateMap state = RequestStateMap.getInstance(ec);
// do per virtual-request stuff
if (state.get(_REQUEST_TYPE) != null)
{
try
{
_endRequest(ec, state);
}
finally
{
state.remove(_REQUEST_TYPE);
}
}
}
/**
* Returns an externalContext for this configurator and all of the configurator services. If this
* method is executed before {@link #beginRequest(ExternalContext)} then this method will call
* beginRequest(). It is important to note, however, that even though beginRequest does not need
* to be explicitly called, {{@link #endRequest(ExternalContext)} does need to be called when the
* request has completed or the contract to the configurators will be broken.
*
* @param ec
* the ExternalContext object that should be wrapped.
*
* @return a decorated ExternalContext object
*/
@Override
public ExternalContext getExternalContext(ExternalContext ec)
{
RequestStateMap state = RequestStateMap.getInstance(ec);
RequestType type = (RequestType) state.get(_REQUEST_TYPE);
//Install the URLEncoder plugin system
ec = new URLEncoderExternalContext(ec);
if (type == null)
{
beginRequest(ec);
type = (RequestType) state.get(_REQUEST_TYPE);
}
else if (!ExternalContextUtils.getRequestType(ec).equals(type))
{
throw new IllegalStateException("The expected request type is not the same as the current request type.");
}
if (!_isDisabled(ec))
{
if (!type.isPortlet() && _isSetRequestBugPresent(ec))
{
//This handles bug 493 against the JSF-RI 1.2_03 and earlier. If the bug
//is present in the current system, add a wrapper to fix it
//TODO sobryan this is somewhat inefficient so should be removed when we
//are no longer dependant on JSF1.2_03 or earlier. Still, we only wrap
//when we have to so it should be no biggy under normal circumstances.
ec = new ClearRequestExternalContext(ec);
}
// Wrap ExternalContexts
for (Configurator config: _services)
{
ec = config.getExternalContext(ec);
}
}
//After all this request wrapping there is just one more thing we want to handle. IF we are not in a PPR
//request AND we are in a processAction, we need to be able to track when a redirect is performed. The
//reason for this is that if a redirect is performed during the processAction then we need to remember this
//so that we can call the endRequest method when its run. Unlike other request, this WILL call the endRequest
//at the end of ProcessAction so the subsequent render will start fresh. In the PPR case, and the case where
//we do not have a performAction, we can skip this wrapper. This information will be saved and removed from
//the request.
if (RequestType.ACTION.equals(type))
{
//We have an action, add the wrapper.
ec = new RecordRedirectExternalContext(ec);
}
return ec;
}
/**
* Initializes the global configurator and the configurator services. This method need not be
* called directly as it will be called from {@link #beginRequest(ExternalContext)} if needed. It
* is also possible to execute this method more then once, although if initialization has already
* happened then a call to this method will not do anything. To re-initialize this class, call
* {@link #destroy()} first and then call this method.
*
* @param ec
* the externalContext needed to initialize this class
*
* @see org.apache.myfaces.trinidad.config.Configurator#init(javax.faces.context.ExternalContext)
*/
@Override
public void init(ExternalContext ec)
{
assert ec != null;
if (!_initialized.get())
{
try
{
//For thread saftey. It is possible for two threads to enter this code at the same time. When
//the do the second one will wait at the lock until initialization is complete. Then the AtomicBoolean
//is checked again for validity.
_initLock.lock();
//Check the AtomicBoolean for a change
if (!_initialized.get())
{
_services = ClassLoaderUtils.getServices(Configurator.class.getName());
// set up the RequestContext Factory as needed.
_setupRequestContextFactory();
// Create a new SkinFactory if needed.
// SkinFactory is now deprecated. For backward compatibility, SkinFactory internally
// calls SkinProvider. So when we init SkinFactory we need to init SkinProvider as well.
// SkinProviderRegistry is the implementation of SkinProvider. This in turn needs other
// internal SkinProviders such as ExternalSkinProvider, TrinidadSkinProvider to be
// available. So we init all of that one by one.
if (SkinFactory.getFactory() == null)
{
SkinFactory.setFactory(new SkinFactoryImpl());
}
// init external skin provider before init of SkinProviderRegistry
Object externalSkinProvider = ec.getApplicationMap()
.get(ExternalSkinProvider.EXTERNAL_SKIN_PROVIDER_KEY);
if (externalSkinProvider == null)
ec.getApplicationMap().put(ExternalSkinProvider.EXTERNAL_SKIN_PROVIDER_KEY,
new ExternalSkinProvider());
// init trinidad skin provider before init of SkinProviderRegistry
Object trinidadSkinProvider = ec.getApplicationMap()
.get(TrinidadSkinProvider.TRINDIAD_SKIN_PROVIDER_KEY);
if (trinidadSkinProvider == null)
ec.getApplicationMap().put(TrinidadSkinProvider.TRINDIAD_SKIN_PROVIDER_KEY,
new TrinidadSkinProvider());
// init skin provider
Object provider = ec.getApplicationMap()
.get(SkinProvider.SKIN_PROVIDER_INSTANCE_KEY);
if (provider == null)
ec.getApplicationMap().put(SkinProvider.SKIN_PROVIDER_INSTANCE_KEY,
new SkinProviderRegistry());
// init the config property service
ConfigPropertyServiceImpl.initialize(ec);
for (final Configurator config: _services)
{
config.init(ec);
}
// we do not register the skin extensions found in trinidad-skins.xml eagerly.
// with SkinProvider SPI we are lazy loading skins as and when required
_initialized.set(true);
}
}
finally
{
//Do cleanup of anything which may have use the thread local manager during
//init.
_releaseManagedThreadLocals();
_initLock.unlock();
}
}
else
{
_LOG.warning("CONFIGURATOR_SERVICES_INITIALIZED");
}
}
/**
* @inheritDoc
*/
@Override
@Deprecated
public void reloadSkins(ExternalContext externalContext, SkinFactory skinFactory)
{
// ask all subordinate configurators to reload their skins
// We do not load the base skins and trinidad-skins eagerly.
// When any configurator tries to register its skins to the SKinFactory,
// it may be calling skinFactory.getSkin(...) to get hold of a base skin.
// As SkinFactory is now under SkinProvider SPI, it makes sure that internally all parents are lazy loaded.
// thus the caller will get a base Skin which he wants, but we do not load all skins eagerly.
for (final Configurator config: _services)
{
config.reloadSkins(externalContext, skinFactory);
}
}
/**
* Hackily called by the ThreadLocalResetter to register itself so that the
* GlobalConfiguratorImpl can tell the ThreadLocalResetter to clean up the
* ThreadLocals at the appropriate time.
*/
void __setThreadLocalResetter(ThreadLocalResetter resetter)
{
if (resetter == null)
throw new NullPointerException();
_threadResetter.set(resetter);
}
/**
* Setup request context factory as needed.
*/
private void _setupRequestContextFactory()
{
if (RequestContextFactory.getFactory() != null)
return;
RequestContextFactory requestContextFactory = null;
List<RequestContextFactory> factories = ClassLoaderUtils.getServices(RequestContextFactory.class.getName());
;
if (factories.isEmpty())
requestContextFactory = new RequestContextFactoryImpl();
else
requestContextFactory = factories.get(0);
RequestContextFactory.setFactory(requestContextFactory);
}
/**
* @param ec
* @return
*/
@SuppressWarnings("unchecked")
private void _attachRequestContext(ExternalContext ec)
{
// If someone didn't release the RequestContext on an earlier request,
// then it'd still be around, and trying to create a new one
// would trigger an exception. We don't want to take down
// this thread for all eternity, so clean up after poorly-behaved code.
RequestContext context = RequestContext.getCurrentInstance();
if (context != null)
{
if (_LOG.isWarning())
{
_LOG.warning("REQUESTCONTEXT_NOT_PROPERLY_RELEASED");
}
_releaseRequestContext(ec);
}
// See if we've got a cached RequestContext instance; if so,
// reattach it
Object cachedRequestContext = RequestStateMap.getInstance(ec).get(_REQUEST_CONTEXT);
// Catch both the null scenario and the
// RequestContext-from-a-different-classloader scenario
if (cachedRequestContext instanceof RequestContext)
{
context = (RequestContext) cachedRequestContext;
context.attach();
}
else
{
RequestContextFactory factory = RequestContextFactory.getFactory();
assert factory != null;
context = factory.createContext(ec);
RequestStateMap.getInstance(ec).put(_REQUEST_CONTEXT, context);
}
}
private void _endRequest(ExternalContext ec, RequestStateMap state)
{
if (!_isDisabled(ec))
{
try
{
_endConfiguratorServiceRequest(ec, state);
}
finally
{
_releaseRequestState(ec);
}
}
}
private void _endConfiguratorServiceRequest(ExternalContext ec, RequestStateMap state)
{
boolean isRedirected = (null != ec.getRequestMap().remove(_REDIRECT_ISSUED));
try
{
//Only end services at the end of a writable response. This will
//generally be RENDER, RESOURCE, and SERVLET.
//WE had to add a check to see if a redirect was issued in order to handle a bug where endRequest was not
//executed on a redirect.
if ((ExternalContextUtils.isResponseWritable(ec) || isRedirected) &&
!Boolean.TRUE.equals(state.get(_CONFIGURATORS_ABORTED)))
{
_endConfiguratorServices(ec);
}
}
finally
{
//If redirect was issued, we do not want to save the state. Let it burn.. :D
if (!isRedirected)
{
state.saveState(ec);
}
}
}
private void _releaseRequestState(ExternalContext ec)
{
try
{
_releaseThreadLocals(ec);
}
finally
{
// ensure that any deferred ComponentReferences are initialized
_finishComponentReferenceInitialization(ec);
}
}
private void _releaseThreadLocals(ExternalContext ec)
{
try
{
_releaseRequestContext(ec);
}
finally
{
_releaseManagedThreadLocals();
}
}
private void _releaseRequestContext(ExternalContext ec)
{
RequestContext context = RequestContext.getCurrentInstance();
if (context != null)
{
context.release();
}
}
/**
* Ensure that any ThreadLocals initialized during this request are cleared
*/
private void _releaseManagedThreadLocals()
{
ThreadLocalResetter resetter = _threadResetter.get();
if (resetter != null)
{
resetter.__removeThreadLocals();
}
}
/**
* Ensure that all DeferredComponentReferences are fully initialized before the
* request completes
*/
private void _finishComponentReferenceInitialization(ExternalContext ec)
{
Map<String, Object> requestMap = ec.getRequestMap();
Collection<ComponentReference<?>> initializeList =
(Collection<ComponentReference<?>>) requestMap.get(_FINISH_INITIALIZATION_LIST_KEY);
if ((initializeList != null) && !initializeList.isEmpty())
{
RuntimeException initializationException = null;
for (ComponentReference<?> reference: initializeList)
{
try
{
reference.ensureInitialization();
}
catch (RuntimeException rte)
{
initializationException = rte;
}
}
// we've initialized everything, so we're done
initializeList.clear();
// rethrow the exception now that we are all done cleaning up
if (initializationException != null)
{
throw initializationException;
}
}
}
private void _endConfiguratorServices(final ExternalContext ec)
{
// Physical request has now ended
// Clear the in-request flag
RequestStateMap.getInstance(ec).remove(_IN_REQUEST);
if (_services != null)
{
for (Configurator config: _services)
{
try
{
config.endRequest(ec);
}
catch (Throwable t)
{
_LOG.severe(t);
}
}
}
}
@SuppressWarnings("unchecked")
private void _startConfiguratorServiceRequest(final ExternalContext ec)
{
// Physical request has now begun
final boolean disabled = isConfiguratorServiceDisabled(ec);
// Tell whether the services were disabled when the requests had begun
RequestStateMap.getInstance(ec).put(_IN_REQUEST, disabled);
// If this hasn't been initialized then please initialize
for (Configurator config: _services)
{
try
{
config.beginRequest(ec);
}
catch (Throwable t)
{
_LOG.severe(t);
}
}
}
private boolean _beginWindowManagerRequest(ExternalContext ec)
{
WindowManager wm = RequestContext.getCurrentInstance().getWindowManager();
boolean cont = true;
try
{
cont = wm.beginRequest(ec);
}
catch (IOException e)
{
_LOG.severe(e);
}
return cont;
}
static private boolean _isSetRequestBugPresent(ExternalContext ec)
{
// This first check is here in order to skip synchronization until
// absolutely necessary.
if (!_sSetRequestBugTested)
{
synchronized (GlobalConfiguratorImpl.class)
{
//This second check is here in case a couple of things enter before the
//boolean is set. This is only an exception case and will make it so
//the initialization code runs only once.
if (!_sSetRequestBugTested)
{
ServletRequest orig = (ServletRequest) ec.getRequest();
// Call getInitParameterMap() up front
ec.getInitParameterMap();
ec.setRequest(new TestRequest(orig));
_sHasSetRequestBug = !TestRequest.isTestParamPresent(ec);
_sSetRequestBugTested = true;
ec.setRequest(orig);
}
}
}
return _sHasSetRequestBug;
}
// This handles an issue with the ExternalContext object prior to
// JSF1.2_04.
static private class ClearRequestExternalContext
extends ExternalContextDecorator
{
private ExternalContext _ec;
private Map<String, Object> _requestCookieMap;
private Map<String, String> _requestHeaderMap;
private Map<String, String[]> _requestHeaderValuesMap;
private Map<String, Object> _requestMap;
private Map<String, String> _requestParameterMap;
private Map<String, String[]> _requestParameterValuesMap;
public ClearRequestExternalContext(ExternalContext ec)
{
_ec = ec;
}
@Override
protected ExternalContext getExternalContext()
{
return _ec;
}
@Override
public void setRequest(Object request)
{
super.setRequest(request);
// And clear out any of the cached maps, since we should
// go back and look in the map
_requestCookieMap = null;
_requestHeaderMap = null;
_requestHeaderValuesMap = null;
_requestMap = null;
_requestParameterMap = null;
_requestParameterValuesMap = null;
}
@Override
public Map<String, Object> getRequestCookieMap()
{
_checkRequest();
if (_requestCookieMap == null)
{
_requestCookieMap = new ServletCookieMap(_getHttpServletRequest());
}
return _requestCookieMap;
}
@Override
public Map<String, String> getRequestHeaderMap()
{
if (_requestHeaderMap == null)
{
_requestHeaderMap = new ServletRequestHeaderMap(_getHttpServletRequest());
}
return _requestHeaderMap;
}
@Override
public Map<String, String[]> getRequestHeaderValuesMap()
{
if (_requestHeaderValuesMap == null)
{
_requestHeaderValuesMap = new ServletRequestHeaderValuesMap(_getHttpServletRequest());
}
return _requestHeaderValuesMap;
}
@Override
public Map<String, Object> getRequestMap()
{
_checkRequest();
if (_requestMap == null)
{
_requestMap = new ServletRequestMap((ServletRequest) getRequest());
}
return _requestMap;
}
@Override
public Map<String, String> getRequestParameterMap()
{
_checkRequest();
if (_requestParameterMap == null)
{
_requestParameterMap = new ServletRequestParameterMap((ServletRequest) getRequest());
}
return _requestParameterMap;
}
@Override
public Map<String, String[]> getRequestParameterValuesMap()
{
_checkRequest();
if (_requestParameterValuesMap == null)
{
_requestParameterValuesMap = new ServletRequestParameterValuesMap((ServletRequest) getRequest());
}
return _requestParameterValuesMap;
}
private void _checkRequest()
{
if (super.getRequest() == null)
{
throw new UnsupportedOperationException("Request is null on this context.");
}
}
private HttpServletRequest _getHttpServletRequest()
{
_checkRequest();
if (!(getRequest() instanceof HttpServletRequest))
{
throw new IllegalArgumentException("Only HttpServletRequest supported");
}
return (HttpServletRequest) getRequest();
}
}
static private class RecordRedirectExternalContext
extends ExternalContextDecorator
{
public RecordRedirectExternalContext(ExternalContext ec)
{
assert (ec != null);
_ec = ec;
}
@Override
public void redirect(String url)
throws IOException
{
super.redirect(url);
//We set a parameter on the request saying that we indeed have a redirect.
_ec.getRequestMap().put(_REDIRECT_ISSUED, AppliedClass.APPLIED);
}
@Override
protected ExternalContext getExternalContext()
{
return _ec;
}
private ExternalContext _ec;
}
private static volatile boolean _sSetRequestBugTested = false;
private static boolean _sHasSetRequestBug = false;
private final ReentrantLock _initLock = new ReentrantLock();
private AtomicBoolean _initialized = new AtomicBoolean(false);
private List<Configurator> _services;
static private final Map<ClassLoader, GlobalConfiguratorImpl> _CONFIGURATORS =
new HashMap<ClassLoader, GlobalConfiguratorImpl>();
static private final String _IN_REQUEST = GlobalConfiguratorImpl.class.getName() + ".IN_REQUEST";
static private final String _REQUEST_CONTEXT = GlobalConfiguratorImpl.class.getName() + ".REQUEST_CONTEXT";
static private final String _REQUEST_TYPE = GlobalConfiguratorImpl.class.getName() + ".REQUEST_TYPE";
static private final String _CONFIGURATORS_ABORTED =
GlobalConfiguratorImpl.class.getName() + ".CONFIGURATORS_ABORTED";
//This should be saved on the ManagedRequestScope and needs to implement
//@ExcludeFromManagedRequestScope to be totally safe
static private final String _REDIRECT_ISSUED = GlobalConfiguratorImpl.class.getName() + ".REDIRECT_ISSUED";
//This will ensure the property is removed on the next request. It should be used
//as the value for _REDIRECT_ISSUED.
@ExcludeFromManagedRequestScope
static private class AppliedClass
{
static public final AppliedClass APPLIED = new AppliedClass();
}
static private class TestRequest
extends ServletRequestWrapper
{
public TestRequest(ServletRequest request)
{
super(request);
}
@Override
public String getParameter(String string)
{
if (_TEST_PARAM.equals(string))
{
return "passed";
}
return super.getParameter(string);
}
static public final boolean isTestParamPresent(ExternalContext ec)
{
return RequestStateMap.getInstance(ec).get(_TEST_PARAM) != null;
}
static private String _TEST_PARAM = TestRequest.class.getName() + ".TEST_PARAM";
}
// skanky duplication of key from ComponentReference Class
private static final String _FINISH_INITIALIZATION_LIST_KEY =
ComponentReference.class.getName() + "#FINISH_INITIALIZATION";
// hacky reference to the ThreadLocalResetter used to clean up request-scoped
// ThreadLocals
private AtomicReference<ThreadLocalResetter> _threadResetter = new AtomicReference<ThreadLocalResetter>();
static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(GlobalConfiguratorImpl.class);
}