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 }