/*
* 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.orchestra.lib.jsf;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.orchestra.CoreConfig;
import org.apache.myfaces.orchestra.conversation.ConversationContext;
import org.apache.myfaces.orchestra.conversation.ConversationManager;
/**
* RequestHandler that ensures that only one thread is processing
* each ConversationContext at a time.
*
* @since 1.1
*/
class ContextLockRequestHandler implements RequestHandler
{
private Log log = LogFactory.getLog(ContextLockRequestHandler.class);
private ConversationContext context;
private boolean lockAcquired = false;
public void init(FacesContext facesContext) throws FacesException
{
if (getSerializeRequests(facesContext))
{
// Fetch the ConversationManager instance for the current HttpSession.
//
// We do not want to create a ConversationManager instance if one does
// not exist; that would force an HttpSession to be created in webapps
// where Orchestra usage only occurs in a small part of the webapp; if
// the user doesn't visit that part of the app we should not force a
// session and ConversationManager to be created until they do need it.
// Hmm..but and JSF component that writes out a URL will trigger the
// creation of a ConversationManager instance, as the contextId is
// currently encoded into each url (see ConversationRequestParameterProvider).
// So avoiding creating it here is probably not very important..
//
// Note that ConversationManager.getInstance requires the FrameworkAdapter
// to be initialized.
ConversationManager manager = ConversationManager.getInstance(false);
if (manager != null)
{
// Fetch a context for this request if one already exists, and lock it
// so that concurrent requests that affect this context block until
// this request is complete. Not doing so can cause races for resources
// within the current context, such as beans or PersistenceContexts.
//
// But if the request did not explicitly specify a contextId then we
// do NOT create a new context at this point. Doing so would create
// contexts for things like Weblet resource requests, and that context
// would then just hang around unused until it times out!
//
// Note that a request that does not explicitly specify a contextId
// might have one created for it later in the request, eg when an
// orchestra-scoped bean is accessed. However this is not a race
// condition because nothing else can refer to that newly created
// id until the response for this request has been sent back to the
// client browser.
context = manager.getCurrentRootConversationContext();
if (context != null)
{
try
{
if (log.isDebugEnabled())
{
log.debug("Locking context " + context.getId());
}
context.lockInterruptablyForCurrentThread();
lockAcquired = true;
}
catch(InterruptedException e)
{
throw new FacesException(e);
}
}
else
{
log.debug("No conversation context specified for this request");
}
}
else
{
log.debug("No conversation manager exists for this request");
}
}
}
public void deinit() throws FacesException
{
if (context != null)
{
if (lockAcquired == true)
{
if (log.isDebugEnabled())
{
log.debug("Unlocking context " + context.getId());
}
context.unlockForCurrentThread();
}
else
{
log.debug(
"Odd situation: lock never acquired. Perhaps InterruptedException occurred"
+ " while waiting to get the context lock?");
}
}
}
private boolean getSerializeRequests(FacesContext facesContext)
{
ExternalContext ec = facesContext.getExternalContext();
// Check for deprecated setting via the OrchestraServletFilter.
Map reqScope = ec.getRequestMap();
Boolean serializeRequests = (Boolean) reqScope.get(CoreConfig.SERIALIZE_REQUESTS);
if (serializeRequests != null)
{
return serializeRequests.booleanValue();
}
// Check for the normal global init param; true unless "false" is specified
String value = ec.getInitParameter(CoreConfig.SERIALIZE_REQUESTS);
return !"false".equals(value); // NON-NLS
}
}