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