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