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.view;
20  
21  import java.util.logging.Level;
22  import java.util.logging.Logger;
23  
24  import javax.faces.context.FacesContext;
25  import javax.faces.view.ViewDeclarationLanguage;
26  import javax.faces.view.ViewDeclarationLanguageFactory;
27  
28  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
29  import org.apache.myfaces.shared.config.MyfacesConfig;
30  import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguageStrategy;
31  import org.apache.myfaces.view.jsp.JspViewDeclarationLanguageStrategy;
32  
33  /**
34   * This is the default PDL factory used as of JSF 2.0, it tries to use Facelet PDL whenever possible, 
35   * but fallback on JSP if required.
36   * 
37   * @author Simon Lessard (latest modification by $Author: lu4242 $)
38   * @version $Revision: 1409476 $ $Date: 2012-11-14 17:14:40 -0500 (Wed, 14 Nov 2012) $
39   *
40   * @since 2.0
41   */
42  public class ViewDeclarationLanguageFactoryImpl extends ViewDeclarationLanguageFactory
43  {
44      /**
45       * Disable facelets VDL from the current application project. 
46       */
47      @JSFWebConfigParam(since="2.0", defaultValue="false", expectedValues="true,false", group="viewhandler")
48      public static final String PARAM_DISABLE_JSF_FACELET = "javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER";
49  
50      private static final String FACELETS_1_VIEW_HANDLER = "com.sun.facelets.FaceletViewHandler";
51  
52      private static final Logger LOGGER = Logger.getLogger(ViewDeclarationLanguageFactoryImpl.class.getName());
53      
54      private volatile boolean _initialized;
55      private volatile ViewDeclarationLanguageStrategy[] _supportedLanguages;
56      
57      /**
58       * 
59       */
60      public ViewDeclarationLanguageFactoryImpl()
61      {
62          _initialized = false;
63      }
64  
65      /**
66       * {@inheritDoc}
67       */
68      @Override
69      public ViewDeclarationLanguage getViewDeclarationLanguage(String viewId)
70      {
71          //if (viewId == null)
72          //{
73          //    throw new NullPointerException("viewId");
74          //}
75          
76          // TODO: It would be nice to be able to preinitialize the factory. However, since it requires 
77          //       access to the ExternalContext it may not be possible, depending on the loading order 
78          //       in the FactoryFinder. Could use ideas here. -= SL =-
79          if (!_initialized)
80          {
81              initialize();
82          }
83          
84          for (ViewDeclarationLanguageStrategy strategy : _supportedLanguages)
85          {
86              if (strategy.handles(viewId))
87              {
88                  return strategy.getViewDeclarationLanguage();
89              }
90          }
91          
92          // throw new FacesException("Cannot find a valid PDL for view id " + viewId);
93          // It does not have sense to throw an exception in this point. Instead
94          // just return null, to indicate that no VDL can handle the viewId.
95          // For example, in org.apache.myfaces.shared.application.DefaultViewHandlerSupport
96          // first getViewDeclarationLanguage(String viewId) is called and if returns null
97          // try the default strategy (look for a file in web folder).
98          return null;
99      }
100     
101     /**
102      * Initialize the supported view declaration languages.
103      */
104     private synchronized void initialize()
105     {
106         if (!_initialized)
107         {
108             FacesContext context = FacesContext.getCurrentInstance();
109 
110             if (isFacelets2Enabled(context))
111             {
112                 logWarningIfLegacyFaceletViewHandlerIsPresent(context);
113 
114                 if (MyfacesConfig.getCurrentInstance(
115                         context.getExternalContext()).isSupportJSPAndFacesEL())
116                 {
117                     _supportedLanguages = new ViewDeclarationLanguageStrategy[2];
118                     _supportedLanguages[0] = new FaceletViewDeclarationLanguageStrategy();
119                     _supportedLanguages[1] = new JspViewDeclarationLanguageStrategy();
120                 }
121                 else
122                 {
123                     _supportedLanguages = new ViewDeclarationLanguageStrategy[1];
124                     _supportedLanguages[0] = new FaceletViewDeclarationLanguageStrategy();
125                 }
126             }
127             else
128             {
129                 // Support JSP only
130                 _supportedLanguages = new ViewDeclarationLanguageStrategy[1];
131                 _supportedLanguages[0] = new JspViewDeclarationLanguageStrategy();
132             }
133 
134             _initialized = true;
135         }
136     }
137     
138     /**
139      * Determines if the current application uses Facelets-2.
140      * To accomplish that it looks at the init param javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER,
141      * 
142      * @param context the <code>FacesContext</code>
143      * @return <code>true</code> if the current application uses the built in Facelets-2,
144      *         <code>false</code> otherwise (e.g. it uses Facelets-1 or only JSP).
145      */
146     private boolean isFacelets2Enabled(FacesContext context)
147     {
148         String param = context.getExternalContext().getInitParameter(PARAM_DISABLE_JSF_FACELET);
149         boolean facelets2ParamDisabled = (param != null && Boolean.parseBoolean(param.toLowerCase()));
150         
151         return !facelets2ParamDisabled;
152     }
153     
154     /**
155      * If the Facelets-1 ViewHandler com.sun.facelets.FaceletViewHandler is present <b>AND</b>
156      * the new Facelets-2 is <b>NOT</b> disabled, we log a <code>WARNING</code>. 
157      * 
158      * @param context the <code>FacesContext</code>
159      */
160     private void logWarningIfLegacyFaceletViewHandlerIsPresent(FacesContext context)
161     {
162         boolean facelets1ViewHandlerPresent
163                 = context.getApplication().getViewHandler().getClass().getName().equals(FACELETS_1_VIEW_HANDLER);
164 
165         if (facelets1ViewHandlerPresent)
166         {
167             if (LOGGER.isLoggable(Level.WARNING))
168             {
169                 LOGGER.log(Level.WARNING, "Your faces-config.xml contains the " + FACELETS_1_VIEW_HANDLER + " class."
170                     + "\nYou need to remove it since you have not disabled the \"new\" Facelets-2 version with the "
171                     + PARAM_DISABLE_JSF_FACELET + " context parameter");
172             }
173         }
174     }
175 }