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 52 // not exist; that would force an HttpSession to be created in webapps 53 // where Orchestra usage only occurs in a small part of the webapp; if 54 // the user doesn't visit that part of the app we should not force a 55 // session and ConversationManager to be created until they do need it. 56 // Hmm..but and JSF component that writes out a URL will trigger the 57 // creation of a ConversationManager instance, as the contextId is 58 // currently encoded into each url (see ConversationRequestParameterProvider). 59 // So avoiding creating it here is probably not very important.. 60 // 61 // Note that ConversationManager.getInstance requires the FrameworkAdapter 62 // to be initialized. 63 ConversationManager manager = ConversationManager.getInstance(false); 64 if (manager != null) 65 { 66 // Fetch a context for this request if one already exists, and lock it 67 // so that concurrent requests that affect this context block until 68 // this request is complete. Not doing so can cause races for resources 69 // within the current context, such as beans or PersistenceContexts. 70 // 71 // But if the request did not explicitly specify a contextId then we 72 // do NOT create a new context at this point. Doing so would create 73 // contexts for things like Weblet resource requests, and that context 74 // would then just hang around unused until it times out! 75 // 76 // Note that a request that does not explicitly specify a contextId 77 // might have one created for it later in the request, eg when an 78 // orchestra-scoped bean is accessed. However this is not a race 79 // condition because nothing else can refer to that newly created 80 // id until the response for this request has been sent back to the 81 // client browser. 82 context = manager.getCurrentRootConversationContext(); 83 if (context != null) 84 { 85 try 86 { 87 if (log.isDebugEnabled()) 88 { 89 log.debug("Locking context " + context.getId()); 90 } 91 context.lockInterruptablyForCurrentThread(); 92 lockAcquired = true; 93 } 94 catch(InterruptedException e) 95 { 96 throw new FacesException(e); 97 } 98 } 99 else 100 { 101 log.debug("No conversation context specified for this request"); 102 } 103 } 104 else 105 { 106 log.debug("No conversation manager exists for this request"); 107 } 108 } 109 } 110 111 public void deinit() throws FacesException 112 { 113 if (context != null) 114 { 115 if (lockAcquired == true) 116 { 117 if (log.isDebugEnabled()) 118 { 119 log.debug("Unlocking context " + context.getId()); 120 } 121 context.unlockForCurrentThread(); 122 } 123 else 124 { 125 log.debug( 126 "Odd situation: lock never acquired. Perhaps InterruptedException occurred" 127 + " while waiting to get the context lock?"); 128 } 129 } 130 } 131 132 private boolean getSerializeRequests(FacesContext facesContext) 133 { 134 ExternalContext ec = facesContext.getExternalContext(); 135 136 // Check for deprecated setting via the OrchestraServletFilter. 137 Map reqScope = ec.getRequestMap(); 138 Boolean serializeRequests = (Boolean) reqScope.get(CoreConfig.SERIALIZE_REQUESTS); 139 if (serializeRequests != null) 140 { 141 return serializeRequests.booleanValue(); 142 } 143 144 // Check for the normal global init param; true unless "false" is specified 145 String value = ec.getInitParameter(CoreConfig.SERIALIZE_REQUESTS); 146 return !"false".equals(value); // NON-NLS 147 } 148 } 149