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 }