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 16:08:23 -0500 (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 }