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 }