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 javax.faces.FacesException;
22  import javax.faces.context.ExternalContext;
23  
24  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
25  
26  import java.io.UnsupportedEncodingException;
27  import java.util.Locale;
28  
29  /**
30   * A ViewHandler manages the component-tree-creation and component-tree-rendering parts
31   * of a request lifecycle (ie "create view", "restore view" and "render response").
32   * <p>
33   * A ViewHandler is responsible for generating the component tree when a new view is
34   * requested; see method "createView".
35   * <p>
36   * When the user performs a "postback", ie activates a UICommand component within a view,
37   * then the ViewHandler is responsible for recreating a view tree identical to the one
38   * used previously to render that view; see method "restoreView".
39   * <p>
40   * And the ViewHandler is also responsible for rendering the final output to be sent to the
41   * user by invoking the rendering methods on components; see method "renderView".
42   * <p>
43   * This class also isolates callers from the underlying request/response system. In particular,
44   * this class does not explicitly depend upon the javax.servlet apis. This allows JSF to be
45   * used on servers that do not implement the servlet API (for example, plain CGI). 
46   * <p>
47   * Examples:
48   * <ul>
49   * <li>A JSP ViewHandler exists for using "jsp" pages as the presentation technology.
50   * This class then works together with a taghandler class and a jsp servlet class to
51   * implement the methods on this abstract class definition.
52   * <li>A Facelets ViewHandler instead uses an xml file to define the components and
53   * non-component data that make up a specific view. 
54   * </ul>
55   * Of course there is no reason why the "template" needs to be a textual file. A view could
56   * be generated based on data in a database, or many other mechanisms.
57   * <p>
58   * This class is expected to be invoked via the concrete implementation of 
59   * {@link javax.faces.lifecycle.Lifecycle}.
60   * <p>
61   * For the official specification for this class, see 
62   * <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>.
63   *
64   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
65   * @version $Revision: 833810 $ $Date: 2009-11-07 21:40:01 -0500 (Sat, 07 Nov 2009) $
66   */
67  public abstract class ViewHandler
68  {
69      public static final String CHARACTER_ENCODING_KEY = "javax.faces.request.charset";
70      
71      /**
72       * Indicate the default suffix to derive the file URI if extension mapping is used. 
73       */
74      @JSFWebConfigParam(defaultValue=".jsp", since="1.1")
75      public static final String DEFAULT_SUFFIX_PARAM_NAME = "javax.faces.DEFAULT_SUFFIX";
76      public static final String DEFAULT_SUFFIX = ".jsp";
77  
78      /**
79       * @since JSF 1.2
80       */
81      public String calculateCharacterEncoding(javax.faces.context.FacesContext context)
82      {
83          String _encoding = null;
84          ExternalContext externalContext = context.getExternalContext();
85          String _contentType = (String) externalContext.getRequestHeaderMap().get("Content-Type");
86          int _indexOf = _contentType == null ? -1 :_contentType.indexOf("charset");
87          if(_indexOf != -1)
88          {
89              String _tempEnc =_contentType.substring(_indexOf); //charset=UTF-8 
90              _encoding = _tempEnc.substring(_tempEnc.indexOf("=")+1); //UTF-8
91              if (_encoding.length() == 0)
92              {
93                  _encoding = null;
94              }
95          }
96          if (_encoding == null) 
97          {
98              boolean _sessionAvailable = externalContext.getSession(false) != null;
99              if(_sessionAvailable)
100             {
101                 Object _sessionParam = externalContext.getSessionMap().get(CHARACTER_ENCODING_KEY); 
102                 if (_sessionParam != null)
103                 {
104                     _encoding = _sessionParam.toString();
105                 }
106             }
107         }
108         
109         return _encoding;
110     }
111     
112     /**
113      * Return the Locale object that should be used when rendering this view to the
114      * current user.
115      * <p>
116      * Some request protocols allow an application user to specify what locale they prefer
117      * the response to be in. For example, HTTP requests can specify the "accept-language"
118      * header.
119      * <p>
120      * Method {@link javax.faces.application.Application#getSupportedLocales()} defines
121      * what locales this JSF application is capable of supporting.
122      * <p>
123      * This method should match such sources of data up and return the Locale object that
124      * is the best choice for rendering the current application to the current user.
125      */
126     public abstract Locale calculateLocale(javax.faces.context.FacesContext context);
127 
128     /**
129      * Return the id of an available render-kit that should be used to map the JSF
130      * components into user presentation.
131      * <p>
132      * The render-kit selected (eg html, xhtml, pdf, xul, ...) may depend upon the user,
133      * properties associated with the request, etc.
134      */
135     public abstract String calculateRenderKitId(javax.faces.context.FacesContext context);
136 
137     /**
138      * Build a root node for a component tree.
139      * <p>
140      * When a request is received, this method is called if restoreView returns null,
141      * ie this is not a "postback". In this case, a root node is created and then renderView
142      * is invoked. It is the responsibility of the renderView method to build the full
143      * component tree (ie populate the UIViewRoot with descendant nodes).
144      * <p>
145      * This method is also invoked when navigation occurs from one view to another, where
146      * the viewId passed is the id of the new view to be displayed. Again it is the responsibility
147      * of renderView to then populate the viewroot with descendants.
148      * <p>
149      * The locale and renderKit settings are inherited from the current UIViewRoot that is
150      * configured before this method is called. That means of course that they do NOT
151      * get set for GET requests, including navigation that has the redirect flag set.
152      */
153     public abstract javax.faces.component.UIViewRoot createView(javax.faces.context.FacesContext context,
154                                                                 String viewId);
155 
156     /**
157      * Return a URL that a remote system can invoke in order to access the specified view.
158      * <p>
159      * Note that the URL a user enters and the viewId which is invoked can be
160      * different. The simplest difference is a change in suffix (eg url "foo.jsf"
161      * references view "foo.jsp").
162      */
163     public abstract String getActionURL(javax.faces.context.FacesContext context,
164                                         String viewId);
165 
166     /**
167      * Return a URL that a remote system can invoke in order to access the specified resource.
168      * <p>
169      * When path starts with a slash, it is relative to the webapp root. Otherwise it is
170      * relative to the value returned by getActionURL.
171      */
172     public abstract String getResourceURL(javax.faces.context.FacesContext context,
173                                           String path);
174     
175     /**
176      * Method must be called by the JSF impl at the beginning of Phase <i>Restore View</i> of the JSF
177      * lifecycle.
178      * 
179      * @since JSF 1.2
180      */
181     public void initView(javax.faces.context.FacesContext context) throws FacesException
182     {
183         String _encoding = this.calculateCharacterEncoding(context);
184         if(_encoding != null)
185         {
186             try
187             {
188                 context.getExternalContext().setRequestCharacterEncoding(_encoding);
189             }
190             catch(UnsupportedEncodingException uee)
191             {
192                 throw new FacesException(uee);
193             }
194         }
195     }
196     
197     /**
198      * Combine the output of all the components in the viewToRender with data from the
199      * original view template (if any) and write the result to context.externalContext.response.
200      * <p>
201      * Component output is generated by invoking the encodeBegin, encodeChildren (optional)
202      * and encodeEnd methods on relevant components in the specified view. How this is
203      * interleaved with the non-component content of the view template (if any) is left
204      * to the ViewHandler implementation.
205      * <p>
206      * The actual type of the Response object depends upon the concrete implementation of
207      * this class. It will almost certainly be an OutputStream of some sort, but may be
208      * specific to the particular request/response  system in use.
209      * <p>
210      * If the view cannot be rendered (eg due to an error in a component) then a FacesException
211      * is thrown.
212      * <p>
213      * Note that if a "postback" has occurred but no navigation to a different view, then
214      * the viewToRender will be fully populated with components already. However on direct
215      * access to a new view, or when navigation has occurred, the viewToRender will just
216      * contain an empty UIViewRoot object that must be populated with components from
217      * the "view template". 
218      */
219     public abstract void renderView(javax.faces.context.FacesContext context,
220                                     javax.faces.component.UIViewRoot viewToRender)
221             throws java.io.IOException,
222             FacesException;
223 
224     /**
225      * Handle a "postback" request by recreating the component tree that was most recently
226      * presented to the user for the specified view.
227      * <p>
228      * When the user performs a "postback" of a view that has previously been created, ie
229      * is updating the state of an existing view tree, then the view handler must recreate
230      * a view tree identical to the one used previously to render that view to the user,
231      * so that the data received from the user can be compared to the old state and the
232      * correct "changes" detected (well, actually only those components that respond to
233      * input are actually needed before the render phase). 
234      * <p>
235      * The components in this tree will then be given the opportunity to examine new input
236      * data provided by the user, and react in the appropriate manner for that component,
237      * as specified for the JSF lifecycle.
238      * <p>
239      * Much of the work required by this method <i>must</i> be delegated to an instance
240      * of StateManager.
241      * <p>
242      * If there is no record of the current user having been sent the specified view
243      * (ie no saved state information available), then NULL should be returned.
244      * <p>
245      * Note that input data provided by the user may also be used to determine exactly
246      * how to restore this view. In the case of "client side state", information
247      * about the components to be restored will be available here. Even for
248      * "server side state", user input may include an indicator that is used to
249      * select among a number of different saved states available for this viewId and
250      * this user.
251      * <p>
252      * Note that data received from users is inherently untrustworthy; care should be
253      * taken to validate this information appropriately.
254      * <p>
255      * See writeState for more details. 
256      */
257     public abstract javax.faces.component.UIViewRoot restoreView(javax.faces.context.FacesContext context,
258                                                                  String viewId);
259 
260     /**
261      * Write sufficient information to context.externalContext.response in order to
262      * be able to restore this view if the user performs a "postback" using that
263      * rendered response.
264      * <p>
265      * For "client side state saving", sufficient information about the view
266      * state should be written to allow a "restore view" operation to succeed 
267      * later. This does not necessarily mean storing <i>all</i> data about the
268      * current view; only data that cannot be recreated from the "template" for
269      * this view needs to be saved.
270      * <p>
271      * For "server side state saving", this method may write out nothing. Alternately
272      * it may write out a "state identifier" to identify which of multiple saved
273      * user states for a particular view should be selected (or just verify that the
274      * saved state does indeed correspond to the expected one).
275      */
276     public abstract void writeState(javax.faces.context.FacesContext context)
277             throws java.io.IOException;
278 }