Save This Page
Home » trinidad-1.2.8-src-all » org.apache.myfaces.trinidadinternal » application » [javadoc | source]
    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.trinidadinternal.application;
   20   
   21   import java.io.File;
   22   import java.io.InputStream;
   23   import java.io.IOException;
   24   import java.lang.reflect.Constructor;
   25   import java.net.URL;
   26   import java.util.ArrayList;
   27   import java.util.Collections;
   28   import java.util.Enumeration;
   29   import java.util.HashMap;
   30   import java.util.List;
   31   import java.util.Locale;
   32   import java.util.Map;
   33   import java.util.Properties;
   34   
   35   import javax.faces.FacesException;
   36   import javax.faces.application.ViewHandler;
   37   import javax.faces.application.ViewHandlerWrapper;
   38   import javax.faces.component.UIViewRoot;
   39   import javax.faces.context.ExternalContext;
   40   import javax.faces.context.FacesContext;
   41   
   42   import org.apache.myfaces.trinidad.context.RequestContext;
   43   import org.apache.myfaces.trinidad.logging.TrinidadLogger;
   44   import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
   45   import org.apache.myfaces.trinidad.render.InternalView;
   46   import org.apache.myfaces.trinidad.util.Service;
   47   import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
   48   import org.apache.myfaces.trinidadinternal.context.TrinidadPhaseListener;
   49   import org.apache.myfaces.trinidadinternal.share.config.Configuration;
   50   import org.apache.myfaces.trinidadinternal.util.URLUtils;
   51   
   52   /**
   53    * ViewHandler that adds modification detection to the existing ViewHandler,
   54    * assuming that the viewId is a valid resource path.
   55    * <p>
   56    * And now also supports inserting URLs tokens to preserve PageFlowScope.
   57    * <p>
   58    * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/application/ViewHandlerImpl.java#0 $) $Date: 05-jan-2006.13:19:09 $
   59    * @todo Rename something less generic
   60    * @todo Support extension mapping (*.faces)
   61    * @todo The modification detection only works for a single user.  That's
   62    *   OK for now, because it's intended for use while developing
   63    */
   64   public class ViewHandlerImpl extends ViewHandlerWrapper
   65   {
   66     static public final String ALTERNATE_VIEW_HANDLER =
   67       "org.apache.myfaces.trinidad.ALTERNATE_VIEW_HANDLER";
   68   
   69     public ViewHandlerImpl(
   70       ViewHandler delegate)
   71     {
   72       _delegate = delegate;
   73       _timestamps = new HashMap<String, Long>();
   74       _loadInternalViews();
   75     }
   76   
   77     protected ViewHandler getWrapped()
   78     {
   79       return _delegate;
   80     }
   81   
   82     @Override
   83     public UIViewRoot createView(FacesContext context, String viewId)
   84     {
   85       _initIfNeeded(context);
   86   
   87       InternalView internal = _getInternalView(context, viewId);
   88       if (internal != null)
   89       {
   90         UIViewRoot root = internal.createView(context, viewId);
   91         if (root != null)
   92           return root;
   93         // Otherwise, fall through to default processing
   94       }
   95       else if (_checkTimestamp(context))
   96       {
   97         try
   98         {
   99           // Check the timestamp on the physical path
  100           String path = _getPath(viewId);
  101           synchronized (_timestamps)
  102           {
  103             Long ts = _timestamps.get(path);
  104             if (ts != _NOT_FOUND)
  105             {
  106               URL url = context.getExternalContext().getResource(path);
  107               Long modified = _getLastModified(url);
  108               _timestamps.put(path, modified);
  109             }
  110           }
  111         }
  112         catch (IOException e)
  113         {
  114           _LOG.severe(e);
  115         }
  116       }
  117   
  118       return super.createView(context, viewId);
  119     }
  120   
  121     @Override
  122     public String getActionURL(FacesContext context, String viewId)
  123     {
  124       String actionURL = super.getActionURL(context, viewId);
  125       RequestContext afContext = RequestContext.getCurrentInstance();
  126       if (afContext != null)
  127       {
  128         actionURL = afContext.getPageResolver().encodeActionURI(actionURL);
  129         actionURL = afContext.getPageFlowScopeProvider().
  130                        encodeCurrentPageFlowScopeURL(context, actionURL);
  131       }
  132   
  133       return actionURL;
  134     }
  135   
  136     @Override
  137     public String getResourceURL(
  138       FacesContext context,
  139       String       path)
  140     {
  141       return super.getResourceURL(context, path);
  142     }
  143   
  144   
  145     @Override
  146     public void renderView(
  147       FacesContext context,
  148       UIViewRoot   viewToRender) throws IOException, FacesException
  149     {
  150       _initIfNeeded(context);
  151   
  152       // See if there is a possiblity of short-circuiting the current
  153       // Render Response
  154       ExtendedRenderKitService service = _getExtendedRenderKitService(context);
  155       if ((service != null) &&
  156           service.shortCircuitRenderView(context))
  157       {
  158         // Yup, we don't need to do anything
  159         ;
  160       }
  161       else
  162       {
  163         try
  164         {
  165           if (service != null)
  166             service.encodeBegin(context);
  167   
  168           InternalView internal = _getInternalView(context,
  169                                                    viewToRender.getViewId());
  170           if (internal != null)
  171           {
  172             internal.renderView(context, viewToRender);
  173           }
  174           else
  175           {
  176             super.renderView(context, viewToRender);
  177           }
  178   
  179           if (service != null)
  180             service.encodeEnd(context);
  181         }
  182         finally
  183         {
  184           if (service != null)
  185             service.encodeFinally(context);
  186         }
  187       }
  188     }
  189   
  190     @Override
  191     public UIViewRoot restoreView(
  192       FacesContext context,
  193       String       viewId)
  194     {
  195       // If we're being asked to re-run the lifecycle for a "return"
  196       // event, always restore the "launch view", which was set
  197       // over in RequestContextImpl
  198       UIViewRoot launchView = (UIViewRoot)
  199         context.getExternalContext().getRequestMap().get(
  200           RequestContextImpl.LAUNCH_VIEW);
  201   
  202       if (launchView != null)
  203       {
  204         context.getExternalContext().getRequestMap().remove(
  205           RequestContextImpl.LAUNCH_VIEW);
  206         TrinidadPhaseListener.markPostback(context);
  207         return launchView;
  208       }
  209   
  210       InternalView internal = _getInternalView(context, viewId);
  211       if (internal != null)
  212       {
  213         return internal.restoreView(context, viewId);
  214       }
  215   
  216       boolean uptodate = true;
  217   
  218       if (_checkTimestamp(context))
  219       {
  220         try
  221         {
  222           // Check the timestamp on the physical path
  223           String path = _getPath(viewId);
  224           synchronized (_timestamps)
  225           {
  226             Long ts = _timestamps.get(path);
  227             if (ts != _NOT_FOUND)
  228             {
  229               URL url = context.getExternalContext().getResource(path);
  230               Long modified = _getLastModified(url);
  231               if (modified == _NOT_FOUND)
  232               {
  233                 _timestamps.put(path, _NOT_FOUND);
  234               }
  235               else if ((ts == null) ||
  236                        (modified.longValue() > ts.longValue()))
  237               {
  238                 _timestamps.put(path, modified);
  239                 if (ts != null)
  240                 {
  241                   _LOG.fine("View document \"" + path + "\" has been modified, " +
  242                             "ignoring postback for view \"" + viewId +"\"");
  243                 }
  244                 uptodate = false;
  245               }
  246             }
  247           }
  248         }
  249         catch (IOException e)
  250         {
  251           _LOG.severe(e);
  252         }
  253       }
  254   
  255       if (!uptodate)
  256       {
  257         return null;
  258       }
  259   
  260       UIViewRoot result = super.restoreView(context, viewId);
  261       // If we've successfully restored a view, then assume that
  262       // this is a postback request.
  263       if (result != null)
  264       {
  265         TrinidadPhaseListener.markPostback(context);
  266       }
  267   
  268       return result;
  269     }
  270   
  271     @Override
  272     public void writeState(
  273       FacesContext context) throws IOException
  274     {
  275       String viewId = context.getViewRoot().getViewId();
  276       InternalView internal =
  277          _getInternalView(context, viewId);
  278   
  279       // As internal views whether they're stateless.  If they are, don't
  280       // bother writing anything out.
  281       if ((internal != null) && internal.isStateless(context, viewId))
  282         return;
  283   
  284       ExtendedRenderKitService service = _getExtendedRenderKitService(context);
  285       if ((service != null) &&
  286           service.isStateless(context))
  287         return;
  288   
  289       super.writeState(context);
  290     }
  291   
  292     synchronized private void _initIfNeeded(FacesContext context)
  293     {
  294       if (!_inited)
  295       {
  296         _inited = true;
  297         String alternateViewHandler =
  298           context.getExternalContext().getInitParameter(ALTERNATE_VIEW_HANDLER);
  299         if (alternateViewHandler != null)
  300         {
  301           ViewHandler viewHandlerInstance = null;
  302           try
  303           {
  304             ClassLoader loader = Thread.currentThread().getContextClassLoader();
  305             Class<?> c = loader.loadClass(alternateViewHandler);
  306             try
  307             {
  308               Constructor<?> constructor = c.getConstructor(
  309                  new Class[]{ViewHandler.class});
  310               viewHandlerInstance =
  311                  (ViewHandler) constructor.newInstance(new Object[]{_delegate});
  312             }
  313             catch (NoSuchMethodException nsme)
  314             {
  315               viewHandlerInstance = (ViewHandler) c.newInstance();
  316             }
  317           }
  318           catch (Exception e)
  319           {
  320             _LOG.warning("CANNOT_LOAD_VIEWHANDLER", alternateViewHandler);
  321             _LOG.warning(e);
  322           }
  323   
  324           if (viewHandlerInstance != null)
  325             _delegate = viewHandlerInstance;
  326         }
  327       }
  328     }
  329   
  330     private ExtendedRenderKitService _getExtendedRenderKitService(
  331       FacesContext context)
  332     {
  333       return Service.getService(context.getRenderKit(),
  334                                 ExtendedRenderKitService.class);
  335     }
  336   
  337     private boolean _checkTimestamp(FacesContext context)
  338     {
  339       if (_checkTimestamp == null)
  340       {
  341         String checkTimestamp =
  342           context.getExternalContext().getInitParameter(Configuration.CHECK_TIMESTAMP_PARAM);
  343         // Detect when we're running inside of the JDeveloper embedded OC4J
  344         // environment - and there, always use timestamp checking
  345         // TODO: come up with a non-proprietary way of checking this?
  346         boolean performCheck = "true".equals(checkTimestamp) ||
  347           "development".equals(System.getProperty("oracle.application.environment"));
  348         _checkTimestamp = Boolean.valueOf(performCheck);
  349         if ("true".equals(checkTimestamp))
  350         {
  351           _LOG.info("TIMESTAMP_CHECKING_ENABLED_SHOULDNOT_IN_PRODUCTION",
  352                     Configuration.CHECK_TIMESTAMP_PARAM);
  353         }
  354       }
  355   
  356       return _checkTimestamp.booleanValue();
  357     }
  358   
  359   
  360     /**
  361      * Return the physical path of a particular URI
  362      */
  363     static private String _getPath(String uri)
  364     {
  365       RequestContext afc = RequestContext.getCurrentInstance();
  366       if (afc != null)
  367       {
  368         return afc.getPageResolver().getPhysicalURI(uri);
  369       }
  370   
  371       // No RequestContext?  Just return the URI
  372       return uri;
  373     }
  374   
  375   
  376     private Long _getLastModified(URL url) throws IOException
  377     {
  378       if (url == null)
  379         return _NOT_FOUND;
  380   
  381       return Long.valueOf(URLUtils.getLastModified(url));
  382     }
  383   
  384   
  385     private InternalView _getInternalView(
  386       FacesContext context, 
  387       String       viewId)
  388     {
  389       InternalView internal = _internalViews.get(viewId);
  390       if (internal == null)
  391       {
  392         // If we're using suffix-mapping, then any internal viewId will
  393         // get affixed with ".jsp" or ".jspx";  try trimming that off
  394         // if present
  395         ExternalContext external = context.getExternalContext();
  396         
  397         // Only bother when using suffix-mapping (path info will always
  398         // be non-null for prefix-mapping)
  399         if (external.getRequestPathInfo() == null)
  400         {
  401           String suffix = external.getInitParameter("javax.faces.DEFAULT_SUFFIX");
  402           if (suffix == null)
  403             suffix = ".jspx";
  404           
  405           if (viewId.endsWith(suffix))
  406           {
  407             String viewIdWithoutSuffix = viewId.substring(
  408                0, viewId.length() - suffix.length());
  409             internal = _internalViews.get(viewIdWithoutSuffix);
  410           }
  411         }
  412       }
  413   
  414       return internal;
  415     }
  416   
  417     //
  418     // Load the META-INF/org.apache.myfaces.trinidad.render.InternalView.properties
  419     // files.
  420     //
  421     private void _loadInternalViews()
  422     {
  423       _internalViews = new HashMap<String, InternalView>();
  424       List<URL> list = new ArrayList<URL>();
  425       ClassLoader loader = _getClassLoader();
  426       try
  427       {
  428         Enumeration<URL> en = loader.getResources(
  429                  "META-INF/org.apache.myfaces.trinidad.render.InternalView.properties");
  430         while (en.hasMoreElements())
  431         {
  432           list.add(en.nextElement());
  433         }
  434   
  435         // And, for some temporary backwards compatibility, also load
  436         // the incorrect properties without "render"
  437         en = loader.getResources(
  438                  "META-INF/org.apache.myfaces.trinidad.InternalView.properties");
  439         while (en.hasMoreElements())
  440         {
  441           list.add(en.nextElement());
  442         }
  443   
  444   
  445         // Reverse the list so it is in the proper order (most local
  446         // entry "wins")
  447         Collections.reverse(list);
  448       }
  449       catch (IOException ioe)
  450       {
  451         _LOG.severe(ioe);
  452       }
  453   
  454       for (URL url : list)
  455       {
  456         try
  457         {
  458           Properties properties = new Properties();
  459           _LOG.fine("Loading internal views from {0}",  url);
  460           InputStream is = url.openStream();
  461           try
  462           {
  463             properties.load(is);
  464           }
  465           finally
  466           {
  467             is.close();
  468           }
  469   
  470           for (Map.Entry<Object, Object> entry : properties.entrySet())
  471           {
  472             String name = (String) entry.getKey();
  473             String className = (String) entry.getValue();
  474             Class<?> clazz = loader.loadClass(className);
  475             InternalView view = (InternalView) clazz.newInstance();
  476             _internalViews.put(name, view);
  477           }
  478         }
  479         catch (IllegalAccessException iae)
  480         {
  481           _LOG.severe("CANNOT_LOAD_URL", url);
  482           _LOG.severe(iae);
  483         }
  484         catch (InstantiationException ie)
  485         {
  486           _LOG.severe("CANNOT_LOAD_URL", url);
  487           _LOG.severe(ie);
  488         }
  489         catch (ClassNotFoundException cnfe)
  490         {
  491           _LOG.severe("CANNOT_LOAD_URL", url);
  492           _LOG.severe(cnfe);
  493         }
  494         catch (IOException ioe)
  495         {
  496           _LOG.severe("CANNOT_LOAD_URL", url);
  497           _LOG.severe(ioe);
  498         }
  499       }
  500     }
  501   
  502   
  503     static private ClassLoader _getClassLoader()
  504     {
  505       ClassLoader loader = Thread.currentThread().getContextClassLoader();
  506       if (loader == null)
  507         loader = ViewHandlerImpl.class.getClassLoader();
  508       return loader;
  509     }
  510   
  511     private Boolean           _checkTimestamp;
  512     // Mostly final, but see _initIfNeeded()
  513     private ViewHandler       _delegate;
  514     private final Map<String, Long> _timestamps;
  515     private boolean           _inited;
  516     private Map<String, InternalView> _internalViews;
  517   
  518     private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(ViewHandlerImpl.class);
  519     private static final Long   _NOT_FOUND = Long.valueOf(0);
  520   }

Save This Page
Home » trinidad-1.2.8-src-all » org.apache.myfaces.trinidadinternal » application » [javadoc | source]