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 ContextLockRequestHandler());
80          handlers.add(new FrameworkAdapterRequestHandler());
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...
103             {
104                 log.debug("getFacesContext: running inner constructor");
105                 ListIterator i = handlers.listIterator();
106                 try
107                 {
108                     while(i.hasNext())
109                     {
110                         RequestHandler h = (RequestHandler) i.next();
111                         
112                         if (log.isDebugEnabled())
113                         {
114                             log.debug("Running inithandler of type " + h.getClass().getName());
115                         }
116 
117                         h.init(facesContext);
118                     }
119                 }
120                 catch(RuntimeException e)
121                 {
122                     _release(i);
123                     throw e;
124                 }
125             }
126 
127             public void release()
128             {
129                 log.debug("Running release");
130                 super.release();
131                 ListIterator i = handlers.listIterator();
132                 // ecch, wind forward to end of list.
133                 while (i.hasNext())
134                 {
135                     i.next();
136                 }
137                 _release(i);
138                 log.debug("Release completed");
139             }
140             
141             private void _release(ListIterator i)
142             {
143                 while (i.hasPrevious())
144                 {
145                     try
146                     {
147                         RequestHandler h = (RequestHandler) i.previous();
148                         h.deinit();
149                     }
150                     catch(Exception e)
151                     {
152                         // ignore errors, so we always deinitialise anything
153                         // that we initialised.
154                         log.error("Problem deinitialising RequestHandler", e);
155                     }
156                 }
157             }
158         };
159     }
160 }