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 log.debug("getFacesContext: entry"); 70 final FacesContext facesContext = original.getFacesContext(context, request, response, lifecycle); 71 if (facesContext == null) 72 { 73 // should not happen 74 return null; 75 } 76 77 if (!ExternalContextUtils.getRequestType(context, request).isPortlet()) 78 { 79 // The handlers need to be reset on every request, as each request has a different 80 // url which could potentially affect the list of handlers for that request. 81 final LinkedList handlers = new LinkedList(); 82 handlers.add(new FrameworkAdapterRequestHandler()); 83 handlers.add(new ContextLockRequestHandler()); 84 handlers.add(new ConversationManagerRequestHandler()); 85 handlers.add(new DataSourceLeakRequestHandler()); 86 87 // Add any other handlers registered by filters or similar 88 Map reqScope = facesContext.getExternalContext().getRequestMap(); 89 handlers.addAll(ConfigUtils.getRequestHandlers(reqScope)); 90 91 // Create and return the custom instance. Note that install=false 92 // is specified for the FacesContextWrapper constructor, meaning 93 // that the wrapper does not bind itself to the current thread: 94 // the original object will still be the one that is returned 95 // by FacesContext.getCurrentInstance(), not this wrapper. What 96 // is important here is that the FacesServlet calls the release 97 // method on this particular object, and that will happen because 98 // FacesServlet uses the return value from this method as the object 99 // to call release on, even though it is not the "current instance". 100 // Not making the wrapper the current instance saves a little bit 101 // of performance.. 102 log.debug("getFacesContext: creating custom instance"); 103 return new _FacesContextWrapper(facesContext, false) 104 { 105 // Constructor. Note that the parent constructor runs first, 106 // which means that FacesContext.currentInstance is valid 107 // at the time that the RequestHandler objects run. 108 { 109 log.debug("getFacesContext: running inner constructor"); 110 ListIterator i = handlers.listIterator(); 111 try 112 { 113 while(i.hasNext()) 114 { 115 RequestHandler h = (RequestHandler) i.next(); 116 117 if (log.isDebugEnabled()) 118 { 119 log.debug("Running inithandler of type " + h.getClass().getName()); 120 } 121 122 h.init(facesContext); 123 } 124 } 125 catch(RuntimeException e) 126 { 127 // Oops, something went wrong. Undo any processing done by the 128 // RequestHandlers that have successfully run so far. 129 // 130 // Warning:: this tries to run deinit on the object that just 131 // failed to initialise. RequestHandler classes should be written 132 // to correctly handle this. 133 log.error("Problem initialising RequestHandler", e); 134 _release(i); 135 throw e; 136 } 137 } 138 139 public void release() 140 { 141 // As the "setup" code for this inner class runs after the 142 // parent class constructor, the release code for this 143 // class should run before the parent release. This also 144 // ensures that FacesContext.currentInstance() is still 145 // valid when the RequestHandler objects run. 146 log.debug("Running release"); 147 148 // Here, run the registered RequestHandlers in reverse order. 149 // Unfortunately, there is no ReverseListIterator class, so 150 // instead here we wind an iterator forward to the end of the 151 // list before passing it to _release, which then walks 152 // backwards through the list. Ecch. 153 ListIterator i = handlers.listIterator(); 154 while (i.hasNext()) 155 { 156 i.next(); 157 } 158 _release(i); 159 160 // And invoke the parent release (which will invoke release 161 // on the target instance that this object decorates). 162 log.debug("Release completed"); 163 super.release(); 164 } 165 166 private void _release(ListIterator i) 167 { 168 while (i.hasPrevious()) 169 { 170 try 171 { 172 RequestHandler h = (RequestHandler) i.previous(); 173 174 if (log.isDebugEnabled()) 175 { 176 log.debug("Running deinithandler of type " + h.getClass().getName()); 177 } 178 179 h.deinit(); 180 } 181 catch(Exception e) 182 { 183 // ignore errors, so we always deinitialise anything 184 // that we initialised. 185 log.error("Problem deinitialising RequestHandler", e); 186 } 187 } 188 } 189 }; 190 } 191 else 192 { 193 if (portletOrchestraFacesContextFactory == null) 194 { 195 portletOrchestraFacesContextFactory = new PortletOrchestraFacesContextFactory(); 196 } 197 return portletOrchestraFacesContextFactory.getFacesContext( 198 facesContext, context, request, response); 199 } 200 } 201 }