View Javadoc

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 }