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.Collections; 24 import java.util.List; 25 import java.util.Locale; 26 import java.util.Map; 27 import java.util.Set; 28 import java.util.stream.Stream; 29 30 import javax.faces.FacesException; 31 import javax.faces.component.UIViewRoot; 32 import javax.faces.context.ExternalContext; 33 import javax.faces.context.FacesContext; 34 import javax.faces.view.ViewDeclarationLanguage; 35 36 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; 37 38 /** 39 * A ViewHandler manages the component-tree-creation and component-tree-rendering parts of a request lifecycle (ie 40 * "create view", "restore view" and "render response"). 41 * <p> 42 * A ViewHandler is responsible for generating the component tree when a new view is requested; see method "createView". 43 * <p> 44 * When the user performs a "postback", ie activates a UICommand component within a view, then the ViewHandler is 45 * responsible for recreating a view tree identical to the one used previously to render that view; see method 46 * "restoreView". 47 * <p> 48 * And the ViewHandler is also responsible for rendering the final output to be sent to the user by invoking the 49 * rendering methods on components; see method "renderView". 50 * <p> 51 * This class also isolates callers from the underlying request/response system. In particular, this class does not 52 * explicitly depend upon the javax.servlet apis. This allows JSF to be used on servers that do not implement the 53 * servlet API (for example, plain CGI). 54 * <p> 55 * Examples: 56 * <ul> 57 * <li>A JSP ViewHandler exists for using "jsp" pages as the presentation technology. This class then works together 58 * with a taghandler class and a jsp servlet class to implement the methods on this abstract class definition. 59 * <li>A Facelets ViewHandler instead uses an xml file to define the components and non-component data that make up a 60 * specific view. 61 * </ul> 62 * Of course there is no reason why the "template" needs to be a textual file. A view could be generated based on data 63 * in a database, or many other mechanisms. 64 * <p> 65 * This class is expected to be invoked via the concrete implementation of {@link javax.faces.lifecycle.Lifecycle}. 66 * <p> 67 * For the official specification for this class, see <a 68 * href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>. 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 @JSFWebConfigParam(since="2.2") 100 public static final java.lang.String DISABLE_FACELET_JSF_VIEWHANDLER_PARAM_NAME = 101 "javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER"; 102 103 /** 104 * Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in a 105 * servlet environment to HttpServletResponse.setBufferSize(). 106 */ 107 @JSFWebConfigParam(since = "2.0", alias = "facelets.BUFFER_SIZE", classType = "java.lang.Integer", 108 tags = "performance", defaultValue="1024", 109 desc = "Define the default buffer size value passed to ExternalContext.setResponseBufferResponse() and in " 110 + "a servlet environment to HttpServletResponse.setBufferSize()") 111 public static final java.lang.String FACELETS_BUFFER_SIZE_PARAM_NAME = "javax.faces.FACELETS_BUFFER_SIZE"; 112 113 /** 114 * Set of class names, separated by ';', implementing TagDecorator interface, used to transform 115 * a view definition in a facelet abstract syntax tree, that is used later to generate a component tree. 116 */ 117 @JSFWebConfigParam(since = "2.0", alias = "facelets.DECORATORS") 118 public static final java.lang.String FACELETS_DECORATORS_PARAM_NAME = "javax.faces.FACELETS_DECORATORS"; 119 120 /** 121 * Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine. 122 */ 123 @JSFWebConfigParam(since = "2.0", 124 desc = "Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.", 125 alias = "facelets.LIBRARIES") 126 public static final java.lang.String FACELETS_LIBRARIES_PARAM_NAME = "javax.faces.FACELETS_LIBRARIES"; 127 128 /** 129 * Define the period used to refresh the facelet abstract syntax tree from the view definition file. 130 * 131 * <p>By default is infinite (no active).</p> 132 */ 133 @JSFWebConfigParam(since = "2.0", defaultValue = "-1", alias = "facelets.REFRESH_PERIOD", 134 classType = "java.lang.Long", tags = "performance") 135 public static final java.lang.String FACELETS_REFRESH_PERIOD_PARAM_NAME = "javax.faces.FACELETS_REFRESH_PERIOD"; 136 137 /** 138 * Skip comments found on a facelet file. 139 */ 140 @JSFWebConfigParam(since = "2.0", alias = "facelets.SKIP_COMMENTS") 141 public static final java.lang.String FACELETS_SKIP_COMMENTS_PARAM_NAME = "javax.faces.FACELETS_SKIP_COMMENTS"; 142 143 /** 144 * @since JSF 1.2 145 */ 146 public String calculateCharacterEncoding(FacesContext context) 147 { 148 String encoding = null; 149 ExternalContext externalContext = context.getExternalContext(); 150 String contentType = externalContext.getRequestHeaderMap().get("Content-Type"); 151 int indexOf = contentType == null ? -1 : contentType.indexOf("charset"); 152 if (indexOf != -1) 153 { 154 String tempEnc = contentType.substring(indexOf); // charset=UTF-8 155 encoding = tempEnc.substring(tempEnc.indexOf('=') + 1); // UTF-8 156 if (encoding.length() == 0) 157 { 158 encoding = null; 159 } 160 } 161 if (encoding == null) 162 { 163 boolean sessionAvailable = externalContext.getSession(false) != null; 164 if (sessionAvailable) 165 { 166 Object sessionParam = externalContext.getSessionMap().get(CHARACTER_ENCODING_KEY); 167 if (sessionParam != null) 168 { 169 encoding = sessionParam.toString(); 170 } 171 } 172 } 173 174 return encoding; 175 } 176 177 /** 178 * Return the Locale object that should be used when rendering this view to the current user. 179 * <p> 180 * Some request protocols allow an application user to specify what locale they prefer the response to be in. For 181 * example, HTTP requests can specify the "accept-language" header. 182 * <p> 183 * Method {@link javax.faces.application.Application#getSupportedLocales()} defines what locales this JSF 184 * application is capable of supporting. 185 * <p> 186 * This method should match such sources of data up and return the Locale object that is the best choice for 187 * rendering the current application to the current user. 188 */ 189 public abstract Locale calculateLocale(FacesContext context); 190 191 /** 192 * Return the id of an available render-kit that should be used to map the JSF components into user presentation. 193 * <p> 194 * The render-kit selected (eg html, xhtml, pdf, xul, ...) may depend upon the user, properties associated with the 195 * request, etc. 196 */ 197 public abstract String calculateRenderKitId(FacesContext context); 198 199 /** 200 * Build a root node for a component tree. 201 * <p> 202 * When a request is received, this method is called if restoreView returns null, ie this is not a "postback". In 203 * this case, a root node is created and then renderView is invoked. It is the responsibility of the renderView 204 * method to build the full component tree (ie populate the UIViewRoot with descendant nodes). 205 * <p> 206 * This method is also invoked when navigation occurs from one view to another, where the viewId passed is the id of 207 * the new view to be displayed. Again it is the responsibility of renderView to then populate the viewroot with 208 * descendants. 209 * <p> 210 * The locale and renderKit settings are inherited from the current UIViewRoot that is configured before this method 211 * is called. That means of course that they do NOT get set for GET requests, including navigation that has the 212 * redirect flag set. 213 */ 214 public abstract UIViewRoot createView(FacesContext context, String viewId); 215 216 /** 217 * @param context 218 * @param input 219 * @return 220 * 221 * @since 2.0 222 */ 223 public String deriveViewId(FacesContext context, String input) 224 { 225 //The default implementation of this method simply returns rawViewId unchanged. 226 return input; 227 } 228 229 /** 230 * 231 * @param context 232 * @param rawViewId 233 * @return 234 * @since 2.1 235 */ 236 public String deriveLogicalViewId(FacesContext context, String rawViewId) 237 { 238 return rawViewId; 239 } 240 241 /** 242 * Returns a URL, suitable for encoding and rendering, that (if activated) will cause the JSF 243 * request processing lifecycle for the specified viewId to be executed 244 */ 245 public abstract String getActionURL(FacesContext context, String viewId); 246 247 /** 248 * Return a JSF action URL derived from the viewId argument that is suitable to be used as 249 * the target of a link in a JSF response. Compiliant implementations must implement this method 250 * as specified in section JSF.7.5.2. The default implementation simply calls through to 251 * getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId. 252 * 253 * @param context 254 * @param viewId 255 * @param parameters 256 * @param includeViewParams 257 * @return 258 * 259 * @since 2.0 260 */ 261 public String getBookmarkableURL(FacesContext context, String viewId, Map<String, List<String>> parameters, 262 boolean includeViewParams) 263 { 264 return getActionURL(context, viewId); 265 } 266 267 /** 268 * Return the ViewDeclarationLanguage instance used for this ViewHandler instance. 269 * <P> 270 * The default implementation of this method returns null. 271 * 272 * @param context 273 * @param viewId 274 * @return 275 * 276 * @since 2.0 277 */ 278 public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId) 279 { 280 // TODO: In some places like RestoreViewExecutor, we are calling deriveViewId 281 // TODO: after call restoreViewSupport.calculateViewId 282 // Maybe this method should be called from here, because it is supposed that 283 // calculateViewId "calculates the view id!" 284 285 // here we return null to support pre jsf 2.0 ViewHandlers (e.g. com.sun.facelets.FaceletViewHandler), 286 // because they don't provide any ViewDeclarationLanguage, 287 // but in the default implementation (ViewHandlerImpl) we return vdlFactory.getViewDeclarationLanguage(viewId) 288 return null; 289 } 290 291 /** 292 * Return a JSF action URL derived from the viewId argument that is suitable to be used by 293 * the NavigationHandler to issue a redirect request to the URL using a NonFaces request. 294 * Compiliant implementations must implement this method as specified in section JSF.7.5.2. 295 * The default implementation simply calls through to 296 * getActionURL(javax.faces.context.FacesContext, java.lang.String), passing the arguments context and viewId. 297 * 298 * @param context 299 * @param viewId 300 * @param parameters 301 * @param includeViewParams 302 * @return 303 * 304 * @since 2.0 305 */ 306 public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, 307 boolean includeViewParams) 308 { 309 return getActionURL(context, viewId); 310 } 311 312 /** 313 * Returns a URL, suitable for encoding and rendering, that (if activated) 314 * will retrieve the specified web application resource. 315 */ 316 public abstract String getResourceURL(FacesContext context, String path); 317 318 /** 319 * Initialize the view for the request processing lifecycle. 320 * <P> 321 * This method must be called at the beginning of the Restore View Phase of the Request 322 * Processing Lifecycle. It is responsible for performing any per-request initialization 323 * necessary to the operation of the lifycecle. 324 * <P> 325 * The default implementation must perform the following actions. If 326 * ExternalContext.getRequestCharacterEncoding() returns null, call 327 * calculateCharacterEncoding(javax.faces.context.FacesContext) and pass the result, 328 * if non-null, into the ExternalContext.setRequestCharacterEncoding(java.lang.String) method. 329 * If ExternalContext.getRequestCharacterEncoding() returns non-null take no action. 330 * 331 * @since JSF 1.2 332 */ 333 public void initView(FacesContext context) throws FacesException 334 { 335 String encoding = this.calculateCharacterEncoding(context); 336 if (encoding != null) 337 { 338 try 339 { 340 context.getExternalContext().setRequestCharacterEncoding(encoding); 341 } 342 catch (UnsupportedEncodingException uee) 343 { 344 throw new FacesException(uee); 345 } 346 } 347 } 348 349 /** 350 * Perform whatever actions are required to render the response view to the 351 * response object associated with the current FacesContext. 352 * <P> 353 * Otherwise, the default implementation must obtain a reference to the 354 * ViewDeclarationLanguage for the viewId of the argument viewToRender and call its 355 * ViewDeclarationLanguage.renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot) 356 * method, returning the result and not swallowing any exceptions thrown by that method. 357 */ 358 public abstract void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException; 359 360 /** 361 * Perform whatever actions are required to restore the view associated with the 362 * specified FacesContext and viewId. It may delegate to the restoreView of the 363 * associated StateManager to do the actual work of restoring the view. If there 364 * is no available state for the specified viewId, return null. 365 * <P> 366 * Otherwise, the default implementation must obtain a reference to the 367 * ViewDeclarationLanguage for this viewId and call its 368 * ViewDeclarationLanguage.restoreView(javax.faces.context.FacesContext, java.lang.String) 369 * method, returning the result and not swallowing any exceptions thrown by that method. 370 */ 371 public abstract UIViewRoot restoreView(FacesContext context, String viewId); 372 373 /** 374 * Take any appropriate action to either immediately write out the current state information 375 * (by calling StateManager.writeState(javax.faces.context.FacesContext, java.lang.Object), 376 * or noting where state information should later be written. 377 * <P> 378 * This method must do nothing if the current request is an Ajax request. When responding 379 * to Ajax requests, the state is obtained by calling StateManager.getViewState(javax.faces.context.FacesContext) 380 * and then written into the Ajax response during 381 * final encoding (UIViewRoot.encodeEnd(javax.faces.context.FacesContext). 382 */ 383 public abstract void writeState(FacesContext context) throws IOException; 384 385 /** 386 * @since 2.2 387 * @param urlPattern 388 */ 389 public void addProtectedView(String urlPattern) 390 { 391 } 392 393 /** 394 * @since 2.2 395 * @param urlPattern 396 */ 397 public boolean removeProtectedView(String urlPattern) 398 { 399 return false; 400 } 401 402 /** 403 * @since 2.2 404 * @return 405 */ 406 public Set<String> getProtectedViewsUnmodifiable() 407 { 408 Set<String> set = Collections.emptySet(); 409 return Collections.unmodifiableSet(set); 410 } 411 412 /** 413 * Return a JSF URL that represents a websocket connection for the passed channel and channelToken 414 * 415 * @since 2.3 416 * @param context 417 * @param channelAndToken 418 * @return 419 */ 420 public abstract String getWebsocketURL(FacesContext context, String channelAndToken); 421 422 /** 423 * @since 2.3 424 * @param facesContext 425 * @param path 426 * @param options 427 * @return 428 */ 429 public Stream<java.lang.String> getViews(FacesContext facesContext, String path, ViewVisitOption... options) 430 { 431 return getViews(facesContext, path, Integer.MAX_VALUE, options); 432 } 433 434 435 /** 436 * 437 * @since 2.3 438 * @param facesContext 439 * @param path 440 * @param maxDepth 441 * @param options 442 * @return 443 */ 444 public Stream<java.lang.String> getViews(FacesContext facesContext, String path, 445 int maxDepth, ViewVisitOption... options) 446 { 447 return Stream.empty(); 448 } 449 450 }