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.LinkedList; 22 import java.util.ListIterator; 23 import java.util.Map; 24 25 import javax.faces.FacesException; 26 import javax.faces.context.FacesContext; 27 import javax.faces.context.FacesContextFactory; 28 import javax.faces.lifecycle.Lifecycle; 29 30 import org.apache.commons.logging.Log; 31 import org.apache.commons.logging.LogFactory; 32 33 /** 34 * Setup some aspects of the Orchestra framework whenever a JSF request is being processed. 35 * <p> 36 * The Orchestra jarfile contains a faces-config.xml file that is automatically loaded by 37 * the FacesServlet. It defines this class as the factory that servlet uses to create a 38 * FacesContext object for each request. 39 * <p> 40 * That factory method is used here as a convenient point to initialize any per-request 41 * Orchestra data-structures. Note that this (of course) only initializes Orchestra for 42 * <i>JSF requests</i>; Orchestra is intended to support non-jsf functionality too (eg 43 * plain jsp or servlets), in which case the appropriate initialization for that environment 44 * needs to be configured via some other mechanism. 45 * <p> 46 * This factory fetches the actual FacesContext object from the previous factory in the 47 * chain, then decorates the returned FacesContext object; this means that this class 48 * integrates fine with other libraries that also configure a custom FacesContextFactory. 49 * 50 * @since 1.1 51 */ 52 public class OrchestraFacesContextFactory extends FacesContextFactory 53 { 54 private final Log log = LogFactory.getLog(OrchestraFacesContextFactory.class); 55 private final FacesContextFactory original; 56 private PortletOrchestraFacesContextFactory portletOrchestraFacesContextFactory; 57 58 public OrchestraFacesContextFactory(FacesContextFactory original) 59 { 60 this.original = original; 61 } 62 63 public FacesContext getFacesContext( 64 final Object context, 65 final Object request, 66 final Object response, 67 final Lifecycle lifecycle) throws FacesException 68 { 69 if (log.isDebugEnabled()) 70 { 71 log.debug("getFacesContext: entry"); 72 } 73 final FacesContext facesContext = original.getFacesContext(context, request, response, lifecycle); 74 if (facesContext == null) 75 { 76 // should not happen 77 return null; 78 } 79 80 if (!ExternalContextUtils.getRequestType(context, request).isPortlet()) 81 { 82 // The handlers need to be reset on every request, as each request has a different 83 // url which could potentially affect the list of handlers for that request. 84 final LinkedList handlers = new LinkedList(); 85 handlers.add(new FrameworkAdapterRequestHandler()); 86 handlers.add(new ContextLockRequestHandler()); 87 handlers.add(new ConversationManagerRequestHandler()); 88 handlers.add(new DataSourceLeakRequestHandler()); 89 90 // Add any other handlers registered by filters or similar 91 Map reqScope = facesContext.getExternalContext().getRequestMap(); 92 handlers.addAll(ConfigUtils.getRequestHandlers(reqScope)); 93 94 // Create and return the custom instance. Note that install=false 95 // is specified for the FacesContextWrapper constructor, meaning 96 // that the wrapper does not bind itself to the current thread: 97 // the original object will still be the one that is returned 98 // by FacesContext.getCurrentInstance(), not this wrapper. What 99 // is important here is that the FacesServlet calls the release 100 // method on this particular object, and that will happen because 101 // FacesServlet uses the return value from this method as the object 102 // to call release on, even though it is not the "current instance". 103 // Not making the wrapper the current instance saves a little bit 104 // of performance.. 105 if (log.isDebugEnabled()) 106 { 107 log.debug("getFacesContext: creating custom instance"); 108 } 109 return new _FacesContextWrapper(facesContext, false) 110 { 111 // Constructor. Note that the parent constructor runs first, 112 // which means that FacesContext.currentInstance is valid 113 // at the time that the RequestHandler objects run. 114 { 115 if (log.isDebugEnabled()) 116 { 117 log.debug("getFacesContext: running inner constructor"); 118 } 119 ListIterator i = handlers.listIterator(); 120 try 121 { 122 while(i.hasNext()) 123 { 124 RequestHandler h = (RequestHandler) i.next(); 125 126 if (log.isDebugEnabled()) 127 { 128 log.debug("Running inithandler of type " + h.getClass().getName()); 129 } 130 131 h.init(facesContext); 132 } 133 } 134 catch(RuntimeException e) 135 { 136 // Oops, something went wrong. Undo any processing done by the 137 // RequestHandlers that have successfully run so far. 138 // 139 // Warning:: this tries to run deinit on the object that just 140 // failed to initialise. RequestHandler classes should be written 141 // to correctly handle this. 142 log.error("Problem initialising RequestHandler", e); 143 _release(i); 144 throw e; 145 } 146 } 147 148 public void release() 149 { 150 // As the "setup" code for this inner class runs after the 151 // parent class constructor, the release code for this 152 // class should run before the parent release. This also 153 // ensures that FacesContext.currentInstance() is still 154 // valid when the RequestHandler objects run. 155 if (log.isDebugEnabled()) 156 { 157 log.debug("Running release"); 158 } 159 160 // Here, run the registered RequestHandlers in reverse order. 161 // Unfortunately, there is no ReverseListIterator class, so 162 // instead here we wind an iterator forward to the end of the 163 // list before passing it to _release, which then walks 164 // backwards through the list. Ecch. 165 ListIterator i = handlers.listIterator(); 166 while (i.hasNext()) 167 { 168 i.next(); 169 } 170 _release(i); 171 172 // And invoke the parent release (which will invoke release 173 // on the target instance that this object decorates). 174 if (log.isDebugEnabled()) 175 { 176 log.debug("Release completed"); 177 } 178 super.release(); 179 } 180 181 private void _release(ListIterator i) 182 { 183 while (i.hasPrevious()) 184 { 185 try 186 { 187 RequestHandler h = (RequestHandler) i.previous(); 188 189 if (log.isDebugEnabled()) 190 { 191 log.debug("Running deinithandler of type " + h.getClass().getName()); 192 } 193 194 h.deinit(); 195 } 196 catch(Exception e) 197 { 198 // ignore errors, so we always deinitialise anything 199 // that we initialised. 200 log.error("Problem deinitialising RequestHandler", e); 201 } 202 } 203 } 204 }; 205 } 206 else 207 { 208 if (portletOrchestraFacesContextFactory == null) 209 { 210 portletOrchestraFacesContextFactory = new PortletOrchestraFacesContextFactory(); 211 } 212 return portletOrchestraFacesContextFactory.getFacesContext( 213 facesContext, context, request, response); 214 } 215 } 216 }