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 }