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                     if (log.isDebugEnabled())
107                     {
108                         log.debug("No conversation context specified for this request");
109                     }
110                 }
111             }
112             else
113             {
114                 if (log.isDebugEnabled())
115                 {
116                     log.debug("No conversation manager exists for this request");
117                 }
118             }
119         }
120     }
121 
122     public void deinit() throws FacesException
123     {
124         if (context != null)
125         {
126             if (lockAcquired == true)
127             {
128                 if (log.isDebugEnabled())
129                 {
130                     log.debug("Unlocking context " + context.getId());
131                 }
132                 context.unlockForCurrentThread();
133             }
134             else
135             {
136                 if (log.isDebugEnabled())
137                 {
138                     log.debug("Odd situation: lock never acquired. Perhaps InterruptedException occurred"
139                             + " while waiting to get the context lock?");
140                 }
141             }
142         }
143     }
144 
145     private boolean getSerializeRequests(FacesContext facesContext)
146     {
147         ExternalContext ec = facesContext.getExternalContext();
148 
149         // Check for deprecated setting via the OrchestraServletFilter.
150         Map reqScope = ec.getRequestMap();
151         Boolean serializeRequests = (Boolean) reqScope.get(CoreConfig.SERIALIZE_REQUESTS);
152         if (serializeRequests != null)
153         {
154             return serializeRequests.booleanValue();
155         }
156 
157         // Check for the normal global init param; true unless "false" is specified
158         String value = ec.getInitParameter(CoreConfig.SERIALIZE_REQUESTS);
159         return !"false".equals(value); // NON-NLS
160     }
161 }
162