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 }