View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing,
    13   * software distributed under the License is distributed on an
    14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   * KIND, either express or implied.  See the License for the
    16   * specific language governing permissions and limitations
    17   * under the License.
    18   */
    19  package org.apache.myfaces.orchestra.lib.jsf;
    20
    21  import java.util.Map;
    22
    23  import javax.faces.FacesException;
    24  import javax.faces.context.ExternalContext;
    25  import javax.faces.context.FacesContext;
    26
    27  import org.apache.commons.logging.Log;
    28  import org.apache.commons.logging.LogFactory;
    29  import org.apache.myfaces.orchestra.CoreConfig;
    30  import org.apache.myfaces.orchestra.conversation.ConversationContext;
    31  import org.apache.myfaces.orchestra.conversation.ConversationManager;
    32
    33  /**
    34   * RequestHandler that ensures that only one thread is processing
    35   * each ConversationContext at a time.
    36   *
    37   * @since 1.1
    38   */
    39  class ContextLockRequestHandler implements RequestHandler
    40  {
    41      private Log log = LogFactory.getLog(ContextLockRequestHandler.class);
    42      private ConversationContext context;
    43      private boolean lockAcquired = false;
    44
    45      public void init(FacesContext facesContext) throws FacesException
    46      {
    47          if (getSerializeRequests(facesContext))
    48          {
    49              // Fetch the ConversationManager instance for the current HttpSession.
    50              //
    51              // We do not want to create a ConversationManager instance if one does not exist; that would force an
    52              // HttpSession to be created in webapps where Orchestra usage only occurs in a small part of the webapp;
    53              // if the user doesn't visit that part of the app we should not force a session and ConversationManager
    54              // to be created until they do need it.
    55              //
    56              // We also should avoid creating an HttpSession unless one exists (and creating a ConversationManager
    57              // instance requires a session). This is particularly useful for applications that have cookies turned
    58              // off (ie use a jsessionid value encoded in the url). In this case, weblets requests will not have
    59              // the jsessionid but do trigger the creation of a FacesContext, and therefore run this code. If we
    60              // create a session here, then we will create a separate session for each and every weblets resource
    61              // request - and they will live until the webapp session timeout expires. Bad. Very bad.
    62              //
    63              // Note that if the request being processed includes any code that uses FacesContext.responseWriter
    64              // then that invokes the ConversationRequestParameterProvider. And that always writes out a contextId
    65              // which in turn requires creating a ConversationManager. But there are value requests that run the
    66              //
    67              // Note that ConversationManager.getInstance requires the FrameworkAdapter to be initialized.
    68              ConversationManager manager = ConversationManager.getInstance(false);
    69              if (manager != null)
    70              {
    71                  // Fetch a context for this request if one already exists, and lock it
    72                  // so that concurrent requests that affect this context block until
    73                  // this request is complete. Not doing so can cause races for resources
    74                  // within the current context, such as beans or PersistenceContexts.
    75                  //
    76                  // But if the request did not explicitly specify a contextId then we
    77                  // do NOT create a new context at this point. Doing so would create
    78                  // contexts for things like Weblet resource requests, and that context
    79                  // would then just hang around unused until it times out!
    80                  //
    81                  // Note that a request that does not explicitly specify a contextId
    82                  // might have one created for it later in the request, eg when an
    83                  // orchestra-scoped bean is accessed. However this is not a race
    84                  // condition because nothing else can refer to that newly created
    85                  // id until the response for this request has been sent back to the
    86                  // client browser.
    87                  context = manager.getCurrentRootConversationContext();
    88                  if (context != null)
    89                  {
    90                      try
    91                      {
    92                          if (log.isDebugEnabled())
    93                          {
    94                              log.debug("Locking context " + context.getId());
    95                          }
    96                          context.lockInterruptablyForCurrentThread();
    97                          lockAcquired = true;
    98                      }
    99                      catch(InterruptedException e)
    100                     {
    101                         throw new FacesException(e);
    102                     }
    103                 }
    104                 else
    105                 {
    106                     log.debug("No conversation context specified for this request");
    107                 }
    108             }
    109             else
    110             {
    111                 log.debug("No conversation manager exists for this request");
    112             }
    113         }
    114     }
    115
    116     public void deinit() throws FacesException
    117     {
    118         if (context != null)
    119         {
    120             if (lockAcquired == true)
    121             {
    122                 if (log.isDebugEnabled())
    123                 {
    124                     log.debug("Unlocking context " + context.getId());
    125                 }
    126                 context.unlockForCurrentThread();
    127             }
    128             else
    129             {
    130                 log.debug(
    131                   "Odd situation: lock never acquired. Perhaps InterruptedException occurred"
    132                   + " while waiting to get the context lock?");
    133             }
    134         }
    135     }
    136
    137     private boolean getSerializeRequests(FacesContext facesContext)
    138     {
    139         ExternalContext ec = facesContext.getExternalContext();
    140
    141         // Check for deprecated setting via the OrchestraServletFilter.
    142         Map reqScope = ec.getRequestMap();
    143         Boolean serializeRequests = (Boolean) reqScope.get(CoreConfig.SERIALIZE_REQUESTS);
    144         if (serializeRequests != null)
    145         {
    146             return serializeRequests.booleanValue();
    147         }
    148
    149         // Check for the normal global init param; true unless "false" is specified
    150         String value = ec.getInitParameter(CoreConfig.SERIALIZE_REQUESTS);
    151         return !"false".equals(value); // NON-NLS
    152     }
    153 }
    154