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      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 }