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 javax.faces.application;
20  
21  import java.io.IOException;
22  import java.io.UnsupportedEncodingException;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  
27  import javax.faces.FacesException;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.view.ViewDeclarationLanguage;
32  
33  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
34  
35  /**
36   * A ViewHandler manages the component-tree-creation and component-tree-rendering parts of a request lifecycle (ie
37   * "create view", "restore view" and "render response").
38   * <p>
39   * A ViewHandler is responsible for generating the component tree when a new view is requested; see method "createView".
40   * <p>
41   * When the user performs a "postback", ie activates a UICommand component within a view, then the ViewHandler is
42   * responsible for recreating a view tree identical to the one used previously to render that view; see method
43   * "restoreView".
44   * <p>
45   * And the ViewHandler is also responsible for rendering the final output to be sent to the user by invoking the
46   * rendering methods on components; see method "renderView".
47   * <p>
48   * This class also isolates callers from the underlying request/response system. In particular, this class does not
49   * explicitly depend upon the javax.servlet apis. This allows JSF to be used on servers that do not implement the
50   * servlet API (for example, plain CGI).
51   * <p>
52   * Examples:
53   * <ul>
54   * <li>A JSP ViewHandler exists for using "jsp" pages as the presentation technology. This class then works together
55   * with a taghandler class and a jsp servlet class to implement the methods on this abstract class definition.
56   * <li>A Facelets ViewHandler instead uses an xml file to define the components and non-component data that make up a
57   * specific view.
58   * </ul>
59   * Of course there is no reason why the "template" needs to be a textual file. A view could be generated based on data
60   * in a database, or many other mechanisms.
61   * <p>
62   * This class is expected to be invoked via the concrete implementation of {@link javax.faces.lifecycle.Lifecycle}.
63   * <p>
64   * For the official specification for this class, see <a
65   * href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>.
66   * 
67   * @author Manfred Geiler (latest modification by $Author: struberg $)
68   * @version $Revision: 1188381 $ $Date: 2011-10-24 21:08:23 +0000 (Mon, 24 Oct 2011) $
69   */
70  public abstract class ViewHandler
71  {
72      public static final String CHARACTER_ENCODING_KEY = "javax.faces.request.charset";
73      public static final String DEFAULT_FACELETS_SUFFIX = ".xhtml";
74      public static final String DEFAULT_SUFFIX = ".xhtml .view.xml .jsp";
75      
76      /**
77       * Indicate the default suffixes, separated by spaces to derive the default file URI 
78       * used by JSF to create views and render pages. 
79       */
80      @JSFWebConfigParam(defaultValue=".xhtml .view.xml .jsp", since="1.1", group="viewhandler")
81      public static final String DEFAULT_SUFFIX_PARAM_NAME = "javax.faces.DEFAULT_SUFFIX";
82      
83      /**
84       * The default extension used to handle facelets pages.
85       */
86      @JSFWebConfigParam(defaultValue=".xhtml", since="2.0", group="viewhandler")
87      public static final String FACELETS_SUFFIX_PARAM_NAME = "javax.faces.FACELETS_SUFFIX";
88      
89      /**
90       * Set of extensions handled by facelets, separated by ';'.
91       */
92      @JSFWebConfigParam(since="2.0", group="viewhandler")
93      public static final String FACELETS_VIEW_MAPPINGS_PARAM_NAME = "javax.faces.FACELETS_VIEW_MAPPINGS";
94      // TODO: Notify EG on that last constant. Using the Facelets' param as well for backward compatiblity is 
95      //       silly. If an application uses Facelets then they'll be using facelets.jar. Once they chose to 
96      //       remove that JAR, they ought to be aware that some changes could be needed, like fixing their 
97      //       context-param. -= Simon Lessard =-
98  
99      /**
100      * @since JSF 1.2
101      */
102     public String calculateCharacterEncoding(FacesContext context)
103     {
104         String encoding = null;
105         ExternalContext externalContext = context.getExternalContext();
106         String contentType = externalContext.getRequestHeaderMap().get("Content-Type");
107         int indexOf = contentType == null ? -1 : contentType.indexOf("charset");
108         if (indexOf != -1)
109         {
110             String tempEnc = contentType.substring(indexOf); // charset=UTF-8
111             encoding = tempEnc.substring(tempEnc.indexOf("=") + 1); // UTF-8
112             if (encoding.length() == 0)
113             {
114                 encoding = null;
115             }
116         }
117         if (encoding == null)
118         {
119             boolean sessionAvailable = externalContext.getSession(false) != null;
120             if (sessionAvailable)
121             {
122                 Object sessionParam = externalContext.getSessionMap().get(CHARACTER_ENCODING_KEY);
123                 if (sessionParam != null)
124                 {
125                     encoding = sessionParam.toString();
126                 }
127             }
128         }
129 
130         return encoding;
131     }
132 
133     /**
134      * Return the Locale object that should be used when rendering this view to the current user.
135      * <p>
136      * Some request protocols allow an application user to specify what locale they prefer the response to be in. For
137      * example, HTTP requests can specify the "accept-language" header.
138      * <p>
139      * Method {@link javax.faces.application.Application#getSupportedLocales()} defines what locales this JSF
140      * application is capable of supporting.
141      * <p>
142      * This method should match such sources of data up and return the Locale object that is the best choice for
143      * rendering the current application to the current user.
144      */
145     public abstract Locale calculateLocale(FacesContext context);
146 
147     /**
148      * Return the id of an available render-kit that should be used to map the JSF components into user presentation.
149      * <p>
150      * The render-kit selected (eg html, xhtml, pdf, xul, ...) may depend upon the user, properties associated with the
151      * request, etc.
152      */
153     public abstract String calculateRenderKitId(FacesContext context);
154 
155     /**
156      * Build a root node for a component tree.
157      * <p>
158      * When a request is received, this method is called if restoreView returns null, ie this is not a "postback". In
159      * this case, a root node is created and then renderView is invoked. It is the responsibility of the renderView
160      * method to build the full component tree (ie populate the UIViewRoot with descendant nodes).
161      * <p>
162      * This method is also invoked when navigation occurs from one view to another, where the viewId passed is the id of
163      * the new view to be displayed. Again it is the responsibility of renderView to then populate the viewroot with
164      * descendants.
165      * <p>
166      * The locale and renderKit settings are inherited from the current UIViewRoot that is configured before this method
167      * is called. That means of course that they do NOT get set for GET requests, including navigation that has the
168      * redirect flag set.
169      */
170     public abstract UIViewRoot createView(FacesContext context, String viewId);
171 
172     /**
173      * @param context
174      * @param input
175      * @return
176      * 
177      * @since 2.0
178      */
179     public String deriveViewId(FacesContext context, String input)
180     {
181         //The default implementation of this method simply returns rawViewId unchanged.
182         return input;
183     }
184     
185     /**
186      * 
187      * @param context
188      * @param rawViewId
189      * @return
190      * @since 2.1
191      */
192     public String deriveLogicalViewId(FacesContext context, String rawViewId)
193     {
194         return rawViewId;
195     }
196 
197     /**
198      * Returns a URL, suitable for encoding and rendering, that (if activated) will cause the JSF
199      * request processing lifecycle for the specified viewId to be executed
200      */
201     public abstract String getActionURL(FacesContext context, String viewId);
202 
203     /**
204      * Return a JSF action URL derived from the viewId argument that is suitable to be used as
205      * the target of a link in a JSF response. Compiliant implementations must implement this method
206      * as specified in section JSF.7.5.2. The default implementation simply calls through to
207      * getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId.
208      * 
209      * @param context
210      * @param viewId
211      * @param parameters
212      * @param includeViewParams
213      * @return
214      * 
215      * @since 2.0
216      */
217     public String getBookmarkableURL(FacesContext context, String viewId, Map<String, List<String>> parameters,
218                                      boolean includeViewParams)
219     {
220         return getActionURL(context, viewId);
221     }
222 
223     /**
224      * Return the ViewDeclarationLanguage instance used for this ViewHandler  instance.
225      * <P>
226      * The default implementation of this method returns null.
227      * 
228      * @param context
229      * @param viewId
230      * @return
231      * 
232      * @since 2.0
233      */
234     public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId)
235     {
236         // TODO: In some places like RestoreViewExecutor, we are calling deriveViewId
237         // TODO: after call restoreViewSupport.calculateViewId
238         // Maybe this method should be called from here, because it is supposed that
239         // calculateViewId "calculates the view id!"
240         
241         // here we return null to support pre jsf 2.0 ViewHandlers (e.g. com.sun.facelets.FaceletViewHandler),
242         // because they don't provide any ViewDeclarationLanguage,
243         // but in the default implementation (ViewHandlerImpl) we return vdlFactory.getViewDeclarationLanguage(viewId)
244         return null;
245     }
246 
247     /**
248      * Return a JSF action URL derived from the viewId argument that is suitable to be used by
249      * the NavigationHandler to issue a redirect request to the URL using a NonFaces request.
250      * Compiliant implementations must implement this method as specified in section JSF.7.5.2.
251      * The default implementation simply calls through to
252      * getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId.
253      * 
254      * @param context
255      * @param viewId
256      * @param parameters
257      * @param includeViewParams
258      * @return
259      * 
260      * @since 2.0
261      */
262     public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters,
263                                  boolean includeViewParams)
264     {
265         return getActionURL(context, viewId);
266     }
267 
268     /**
269      * Returns a URL, suitable for encoding and rendering, that (if activated)
270      * will retrieve the specified web application resource.
271      */
272     public abstract String getResourceURL(FacesContext context, String path);
273 
274     /**
275      * Initialize the view for the request processing lifecycle.
276      * <P>
277      * This method must be called at the beginning of the Restore View Phase of the Request
278      * Processing Lifecycle. It is responsible for performing any per-request initialization
279      * necessary to the operation of the lifycecle.
280      * <P>
281      * The default implementation must perform the following actions. If
282      * ExternalContext.getRequestCharacterEncoding() returns null, call
283      * calculateCharacterEncoding(javax.faces.context.FacesContext) and pass the result,
284      * if non-null, into the ExternalContext.setRequestCharacterEncoding(java.lang.String) method.
285      * If ExternalContext.getRequestCharacterEncoding() returns non-null take no action.
286      * 
287      * @since JSF 1.2
288      */
289     public void initView(FacesContext context) throws FacesException
290     {
291         String encoding = this.calculateCharacterEncoding(context);
292         if (encoding != null)
293         {
294             try
295             {
296                 context.getExternalContext().setRequestCharacterEncoding(encoding);
297             }
298             catch (UnsupportedEncodingException uee)
299             {
300                 throw new FacesException(uee);
301             }
302         }
303     }
304 
305     /**
306      *  Perform whatever actions are required to render the response view to the
307      *  response object associated with the current FacesContext.
308      *  <P>
309      *  Otherwise, the default implementation must obtain a reference to the
310      *  ViewDeclarationLanguage for the viewId of the argument viewToRender and call its
311      *  ViewDeclarationLanguage.renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot)
312      *  method, returning the result and not swallowing any exceptions thrown by that method.
313      */
314     public abstract void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException;
315 
316     /**
317      * Perform whatever actions are required to restore the view associated with the
318      * specified FacesContext and viewId. It may delegate to the restoreView of the
319      * associated StateManager to do the actual work of restoring the view. If there
320      * is no available state for the specified viewId, return null.
321      * <P>
322      * Otherwise, the default implementation must obtain a reference to the
323      * ViewDeclarationLanguage for this viewId and call its
324      * ViewDeclarationLanguage.restoreView(javax.faces.context.FacesContext, java.lang.String)
325      * method, returning the result and not swallowing any exceptions thrown by that method.
326      */
327     public abstract UIViewRoot restoreView(FacesContext context, String viewId);
328 
329     /**
330      * Take any appropriate action to either immediately write out the current state information
331      * (by calling StateManager.writeState(javax.faces.context.FacesContext, java.lang.Object),
332      * or noting where state information should later be written.
333      * <P>
334      * This method must do nothing if the current request is an Ajax request. When responding
335      * to Ajax requests, the state is obtained by calling StateManager.getViewState(javax.faces.context.FacesContext)
336      * and then written into the Ajax response during
337      * final encoding (UIViewRoot.encodeEnd(javax.faces.context.FacesContext).
338      */
339     public abstract void writeState(FacesContext context) throws IOException;
340 
341 }