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 org.apache.myfaces.lifecycle; 20 21 22 import javax.faces.context.FacesContext; 23 import java.io.Serializable; 24 import javax.servlet.http.Cookie; 25 import javax.servlet.http.HttpServletResponse; 26 import java.util.Map; 27 28 /** 29 * Contains information about whether the user has 30 * JavaScript enabled on his client, etc. 31 * It also contains the windowhandler html which gets sent to 32 * the browser to detect the current windowId. 33 * 34 * This allows the 'customisation' of this html file to e.g. 35 * adopt the background colour to avoid screen flickering. 36 * 37 * Please note that all subclasses of ClientConfig should also 38 * be @SessionScoped as well! 39 */ 40 public class ClientConfig implements Serializable 41 { 42 private static final long serialVersionUID = 581351549574404793L; 43 44 /** We will set a cookie with this very name if a noscript link got clicked by the user */ 45 public static final String COOKIE_NAME_NOSCRIPT_ENABLED = "mfNoScriptEnabled"; 46 47 private volatile Boolean javaScriptEnabled = null; 48 49 protected String windowHandlerHtml; 50 51 /** lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)} */ 52 private volatile String userAgent = null; 53 54 /** 55 * The location of the default windowhandler resource 56 */ 57 //private static final String DEFAULT_WINDOW_HANDLER_HTML_FILE = "static/windowhandler.html"; 58 59 /** 60 * Defaults to <code>true</code>. 61 * @return if the user has JavaScript enabled 62 */ 63 public boolean isJavaScriptEnabled() 64 { 65 if (javaScriptEnabled == null) 66 { 67 synchronized(this) 68 { 69 // double lock checking idiom on volatile variable works since java5 70 if (javaScriptEnabled == null) 71 { 72 // no info means that it is default -> true 73 javaScriptEnabled = Boolean.TRUE; 74 75 FacesContext facesContext = FacesContext.getCurrentInstance(); 76 if (facesContext != null) 77 { 78 Cookie cookie = (Cookie) facesContext.getExternalContext(). 79 getRequestCookieMap().get(COOKIE_NAME_NOSCRIPT_ENABLED); 80 if (cookie!= null) 81 { 82 javaScriptEnabled = Boolean.parseBoolean((String) cookie.getValue()); 83 } 84 } 85 } 86 } 87 } 88 return javaScriptEnabled; 89 } 90 91 /** 92 * Set it to <code>false</code> if you don't like to use the 93 * JavaScript based client side windowhandler. In this case 94 * the request will be returned directly. 95 * @param javaScriptEnabled 96 */ 97 public void setJavaScriptEnabled(boolean javaScriptEnabled) 98 { 99 this.javaScriptEnabled = javaScriptEnabled; 100 101 // and now also store this information inside a cookie! 102 FacesContext facesContext = FacesContext.getCurrentInstance(); 103 if (facesContext != null) 104 { 105 Object r = facesContext.getExternalContext().getResponse(); 106 if (r instanceof HttpServletResponse) 107 { 108 Cookie cookie = new Cookie(COOKIE_NAME_NOSCRIPT_ENABLED, "" + javaScriptEnabled); 109 cookie.setPath("/"); // for all the server 110 HttpServletResponse response = (HttpServletResponse) r; 111 response.addCookie(cookie); 112 } 113 } 114 } 115 116 /** 117 * For branding the whole windowhandler page - e.g. not only change the 118 * background color, add some images and empty menu structure 119 * or the language of the message text - you can just copy the content of the 120 * {@link #DEFAULT_WINDOW_HANDLER_HTML_FILE} and adopt it to your needs. 121 * 122 * The reason for this is to minimize visual side effects on browsers who do 123 * not properly support html5 localstorage. 124 * 125 * @return the location of the <i>windowhandler.html</i> resource 126 * which should be sent to the users browser. 127 */ 128 /* 129 public String getWindowHandlerResourceLocation() 130 { 131 return DEFAULT_WINDOW_HANDLER_HTML_FILE; 132 }*/ 133 134 /** 135 * This might return different windowhandlers based on user settings like 136 * his language, an affiliation, etc 137 * @return a String containing the whole windowhandler.html file. 138 * @throws IOException 139 */ 140 /* 141 public String getWindowHandlerHtml() throws IOException 142 { 143 if (FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development) 144 && windowHandlerHtml != null) 145 { 146 // use cached windowHandlerHtml except in Development 147 return windowHandlerHtml; 148 } 149 150 InputStream is = ClassUtils.getContextClassLoader().getResourceAsStream( 151 getWindowHandlerResourceLocation()); 152 StringBuffer sb = new StringBuffer(); 153 try 154 { 155 byte[] buf = new byte[16 * 1024]; 156 int bytesRead; 157 while ((bytesRead = is.read(buf)) != -1) 158 { 159 String sbuf = new String(buf, 0, bytesRead); 160 sb.append(sbuf); 161 } 162 } 163 finally 164 { 165 is.close(); 166 } 167 168 windowHandlerHtml = sb.toString(); 169 170 return windowHandlerHtml; 171 }*/ 172 173 174 /** 175 * This information will get stored as it cannot 176 * change during the session anyway. 177 * @return the UserAgent of the request. 178 */ 179 public String getUserAgent(FacesContext facesContext) 180 { 181 if (userAgent == null) 182 { 183 synchronized (this) 184 { 185 if (userAgent == null) 186 { 187 Map<String, String[]> requestHeaders = 188 facesContext.getExternalContext().getRequestHeaderValuesMap(); 189 190 if (requestHeaders != null && 191 requestHeaders.containsKey("User-Agent")) 192 { 193 String[] userAgents = requestHeaders.get("User-Agent"); 194 userAgent = userAgents.length > 0 ? userAgents[0] : null; 195 } 196 } 197 } 198 } 199 200 return userAgent; 201 } 202 203 /** 204 * Users can overload this method to define in which scenarios a request should result 205 * in an 'intercepted' page with proper windowId detection. This can e.g. contain 206 * blacklisting some userAgents. 207 * By default the following User-Agents will be served directly: 208 * <ul> 209 * <li>.*bot.*</li> 210 * <li>.*Bot.*</li> 211 * <li>.*Slurp.*</li> 212 * <li>.*Crawler.*</li> 213 * </ul> 214 * @return <code>true</code> if the Request should get 'intercepted' and the intermediate 215 * windowhandler.html page should get rendered first. By returning <code>false</code> 216 * the requested page will get rendered intermediately. 217 * @see #getUserAgent(javax.faces.context.FacesContext) for determining the UserAgent 218 */ 219 public boolean isClientSideWindowHandlerRequest(FacesContext facesContext) 220 { 221 if (!isJavaScriptEnabled()) 222 { 223 return false; 224 } 225 226 String userAgent = getUserAgent(facesContext); 227 228 if (userAgent != null && 229 ( userAgent.indexOf("bot") >= 0 || // Googlebot, etc 230 userAgent.indexOf("Bot") >= 0 || // BingBot, etc 231 userAgent.indexOf("Slurp") >= 0 || // Yahoo Slurp 232 userAgent.indexOf("Crawler") >= 0 // various other Crawlers 233 ) ) 234 { 235 return false; 236 } 237 238 return true; 239 } 240 241 }