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 }