View Javadoc

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 }