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: bommel $)
68   * @version $Revision: 1187700 $ $Date: 2011-10-22 12:19:37 +0000 (Sat, 22 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 .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 .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      * Returns a URL, suitable for encoding and rendering, that (if activated) will cause the JSF request processing lifecycle for the specified viewId to be executed
187      */
188     public abstract String getActionURL(FacesContext context, String viewId);
189 
190     /**
191      * Return a JSF action URL derived from the viewId argument that is suitable to be used as the target of a link in a JSF response. Compiliant implementations must implement this method as specified in section JSF.7.5.2. The default implementation simply calls through to getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId.
192      * 
193      * @param context
194      * @param viewId
195      * @param parameters
196      * @param includeViewParams
197      * @return
198      * 
199      * @since 2.0
200      */
201     public String getBookmarkableURL(FacesContext context, String viewId, Map<String, List<String>> parameters,
202                                      boolean includeViewParams)
203     {
204         return getActionURL(context, viewId);
205     }
206 
207     /**
208      * Return the ViewDeclarationLanguage instance used for this ViewHandler  instance.
209      * <P>
210      * The default implementation of this method returns null.
211      * 
212      * @param context
213      * @param viewId
214      * @return
215      * 
216      * @since 2.0
217      */
218     public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId)
219     {
220         // TODO: In some places like RestoreViewExecutor, we are calling deriveViewId after call restoreViewSupport.calculateViewId
221         // Maybe this method should be called from here, because it is supposed that calculateViewId "calculates the view id!"
222         
223         // here we return null to support pre jsf 2.0 ViewHandlers (e.g. com.sun.facelets.FaceletViewHandler),
224         // because they don't provide any ViewDeclarationLanguage,
225         // but in the default implementation (ViewHandlerImpl) we return vdlFactory.getViewDeclarationLanguage(viewId)
226         return null;
227     }
228 
229     /**
230      * Return a JSF action URL derived from the viewId argument that is suitable to be used by the NavigationHandler to issue a redirect request to the URL using a NonFaces request. Compiliant implementations must implement this method as specified in section JSF.7.5.2. The default implementation simply calls through to getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId.
231      * 
232      * @param context
233      * @param viewId
234      * @param parameters
235      * @param includeViewParams
236      * @return
237      * 
238      * @since 2.0
239      */
240     public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters,
241                                  boolean includeViewParams)
242     {
243         return getActionURL(context, viewId);
244     }
245 
246     /**
247      * Returns a URL, suitable for encoding and rendering, that (if activated) will retrieve the specified web application resource.
248      */
249     public abstract String getResourceURL(FacesContext context, String path);
250 
251     /**
252      * Initialize the view for the request processing lifecycle.
253      * <P>
254      * This method must be called at the beginning of the Restore View Phase of the Request Processing Lifecycle. It is responsible for performing any per-request initialization necessary to the operation of the lifycecle.
255      * <P>
256      * The default implementation must perform the following actions. If ExternalContext.getRequestCharacterEncoding() returns null, call calculateCharacterEncoding(javax.faces.context.FacesContext) and pass the result, if non-null, into the ExternalContext.setRequestCharacterEncoding(java.lang.String) method. If ExternalContext.getRequestCharacterEncoding() returns non-null take no action.
257      * 
258      * @since JSF 1.2
259      */
260     public void initView(FacesContext context) throws FacesException
261     {
262         String _encoding = this.calculateCharacterEncoding(context);
263         if (_encoding != null)
264         {
265             try
266             {
267                 context.getExternalContext().setRequestCharacterEncoding(_encoding);
268             }
269             catch (UnsupportedEncodingException uee)
270             {
271                 throw new FacesException(uee);
272             }
273         }
274     }
275 
276     /**
277      *  Perform whatever actions are required to render the response view to the response object associated with the current FacesContext.
278      *  <P>
279      *  Otherwise, the default implementation must obtain a reference to the ViewDeclarationLanguage for the viewId of the argument viewToRender and call its ViewDeclarationLanguage.renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot) method, returning the result and not swallowing any exceptions thrown by that method.
280      */
281     public abstract void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException;
282 
283     /**
284      * Perform whatever actions are required to restore the view associated with the specified FacesContext and viewId. It may delegate to the restoreView of the associated StateManager to do the actual work of restoring the view. If there is no available state for the specified viewId, return null.
285      * <P>
286      * Otherwise, the default implementation must obtain a reference to the ViewDeclarationLanguage for this viewId and call its ViewDeclarationLanguage.restoreView(javax.faces.context.FacesContext, java.lang.String) method, returning the result and not swallowing any exceptions thrown by that method.
287      */
288     public abstract UIViewRoot restoreView(FacesContext context, String viewId);
289 
290     /**
291      * Take any appropriate action to either immediately write out the current state information (by calling StateManager.writeState(javax.faces.context.FacesContext, java.lang.Object), or noting where state information should later be written.
292      * <P>
293      * This method must do nothing if the current request is an Ajax request. When responding to Ajax requests, the state is obtained by calling StateManager.getViewState(javax.faces.context.FacesContext) and then written into the Ajax response during final encoding (UIViewRoot.encodeEnd(javax.faces.context.FacesContext). 
294      */
295     public abstract void writeState(FacesContext context) throws IOException;
296 
297 }