Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ClientConfig |
|
| 5.75;5.75 |
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 | 0 | 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 | 0 | private volatile Boolean javaScriptEnabled = null; |
48 | ||
49 | protected String windowHandlerHtml; | |
50 | ||
51 | /** lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)} */ | |
52 | 0 | 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 | 0 | if (javaScriptEnabled == null) |
66 | { | |
67 | 0 | synchronized(this) |
68 | { | |
69 | // double lock checking idiom on volatile variable works since java5 | |
70 | 0 | if (javaScriptEnabled == null) |
71 | { | |
72 | // no info means that it is default -> true | |
73 | 0 | javaScriptEnabled = Boolean.TRUE; |
74 | ||
75 | 0 | FacesContext facesContext = FacesContext.getCurrentInstance(); |
76 | 0 | if (facesContext != null) |
77 | { | |
78 | 0 | Cookie cookie = (Cookie) facesContext.getExternalContext(). |
79 | getRequestCookieMap().get(COOKIE_NAME_NOSCRIPT_ENABLED); | |
80 | 0 | if (cookie!= null) |
81 | { | |
82 | 0 | javaScriptEnabled = Boolean.parseBoolean((String) cookie.getValue()); |
83 | } | |
84 | } | |
85 | } | |
86 | 0 | } |
87 | } | |
88 | 0 | 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 | 0 | this.javaScriptEnabled = javaScriptEnabled; |
100 | ||
101 | // and now also store this information inside a cookie! | |
102 | 0 | FacesContext facesContext = FacesContext.getCurrentInstance(); |
103 | 0 | if (facesContext != null) |
104 | { | |
105 | 0 | Object r = facesContext.getExternalContext().getResponse(); |
106 | 0 | if (r instanceof HttpServletResponse) |
107 | { | |
108 | 0 | Cookie cookie = new Cookie(COOKIE_NAME_NOSCRIPT_ENABLED, "" + javaScriptEnabled); |
109 | 0 | cookie.setPath("/"); // for all the server |
110 | 0 | HttpServletResponse response = (HttpServletResponse) r; |
111 | 0 | response.addCookie(cookie); |
112 | } | |
113 | } | |
114 | 0 | } |
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 | 0 | if (userAgent == null) |
182 | { | |
183 | 0 | synchronized (this) |
184 | { | |
185 | 0 | if (userAgent == null) |
186 | { | |
187 | 0 | Map<String, String[]> requestHeaders = |
188 | facesContext.getExternalContext().getRequestHeaderValuesMap(); | |
189 | ||
190 | 0 | if (requestHeaders != null && |
191 | requestHeaders.containsKey("User-Agent")) | |
192 | { | |
193 | 0 | String[] userAgents = requestHeaders.get("User-Agent"); |
194 | 0 | userAgent = userAgents.length > 0 ? userAgents[0] : null; |
195 | } | |
196 | } | |
197 | 0 | } |
198 | } | |
199 | ||
200 | 0 | 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 | 0 | if (!isJavaScriptEnabled()) |
222 | { | |
223 | 0 | return false; |
224 | } | |
225 | ||
226 | 0 | String userAgent = getUserAgent(facesContext); |
227 | ||
228 | 0 | 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 | 0 | return false; |
236 | } | |
237 | ||
238 | 0 | return true; |
239 | } | |
240 | ||
241 | } |