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 }