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  
20  package org.apache.myfaces.tobago.internal.config;
21  
22  import org.apache.myfaces.tobago.application.ProjectStage;
23  import org.apache.myfaces.tobago.config.TobagoConfig;
24  import org.apache.myfaces.tobago.context.Theme;
25  import org.apache.myfaces.tobago.context.ThemeImpl;
26  import org.apache.myfaces.tobago.internal.util.JndiUtils;
27  import org.apache.myfaces.tobago.sanitizer.Sanitizer;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import javax.faces.application.Application;
32  import javax.faces.context.FacesContext;
33  import javax.naming.Context;
34  import javax.naming.InitialContext;
35  import javax.naming.NamingException;
36  import javax.servlet.ServletContext;
37  import java.util.ArrayList;
38  import java.util.Collections;
39  import java.util.HashMap;
40  import java.util.HashSet;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Set;
44  
45  /**
46   * Implementation of the Tobago configuration.
47   * <p/>
48   * All setters must are protected, so EL can't modify this config.
49   */
50  public class TobagoConfigImpl extends TobagoConfig {
51  
52    private static final Logger LOG = LoggerFactory.getLogger(TobagoConfigImpl.class);
53  
54    private List<Theme> supportedThemes;
55    private List<String> supportedThemeNames;
56    private Theme defaultTheme;
57    private String defaultThemeName;
58    private List<String> resourceDirs;
59    private Map<String, Theme> availableThemes;
60    private RenderersConfig renderersConfig;
61    private ProjectStage projectStage;
62    private boolean createSessionSecret;
63    private boolean checkSessionSecret;
64    private boolean preventFrameAttacks;
65    private ContentSecurityPolicy contentSecurityPolicy;
66    private boolean setNosniffHeader;
67    private Map<String, String> defaultValidatorInfo;
68    private Sanitizer sanitizer;
69    private boolean autoAccessKeyFromLabel;
70    private boolean classicDateTimePicker;
71    private Map<String, String> mimeTypes;
72  
73    private boolean unmodifiable = false;
74  
75    protected TobagoConfigImpl() {
76      supportedThemeNames = new ArrayList<String>();
77      supportedThemes = new ArrayList<Theme>();
78      resourceDirs = new ArrayList<String>();
79      createSessionSecret = true;
80      checkSessionSecret = true;
81      preventFrameAttacks = true;
82      setNosniffHeader = true;
83      contentSecurityPolicy = new ContentSecurityPolicy(ContentSecurityPolicy.Mode.OFF.getValue());
84      autoAccessKeyFromLabel = true;
85      classicDateTimePicker = false;
86      mimeTypes = new HashMap<String, String>();
87    }
88  
89    /**
90     * Lock the configuration, so it cannot be modified any more.
91     */
92    protected void lock() {
93      unmodifiable = true;
94      supportedThemes = Collections.unmodifiableList(supportedThemes);
95      for (Theme theme : supportedThemes) {
96        ((ThemeImpl) theme).lock();
97      }
98      supportedThemeNames = Collections.unmodifiableList(supportedThemeNames);
99      resourceDirs = Collections.unmodifiableList(resourceDirs);
100     availableThemes = Collections.unmodifiableMap(availableThemes);
101 
102     if (renderersConfig instanceof RenderersConfigImpl) {
103       ((RenderersConfigImpl) renderersConfig).lock();
104     }
105     contentSecurityPolicy.lock();
106 
107     mimeTypes = Collections.unmodifiableMap(mimeTypes);
108   }
109 
110   private void checkLocked() throws IllegalStateException {
111     if (unmodifiable) {
112       throw new RuntimeException("The configuration must not be changed after initialization!");
113     }
114   }
115 
116   protected void addSupportedThemeName(final String name) {
117     checkLocked();
118     supportedThemeNames.add(name);
119   }
120 
121   // TODO one init method
122   protected void resolveThemes() {
123     checkLocked();
124     if (defaultThemeName != null) {
125       defaultTheme = availableThemes.get(defaultThemeName);
126       checkThemeIsAvailable(defaultThemeName, defaultTheme);
127       if (LOG.isDebugEnabled()) {
128         LOG.debug("name = '{}'", defaultThemeName);
129         LOG.debug("defaultTheme = '{}'", defaultTheme);
130       }
131     } else {
132       int deep = 0;
133       for (final Map.Entry<String, Theme> entry : availableThemes.entrySet()) {
134         final Theme theme = entry.getValue();
135         if (theme.getFallbackList().size() > deep) {
136           defaultTheme = theme;
137           deep = theme.getFallbackList().size();
138         }
139       }
140       if (defaultTheme == null) {
141         final String error = "Did not found any theme! "
142             + "Please ensure you have a tobago-config.xml with a theme-definition in your "
143             + "theme JAR. Please add a theme JAR to your WEB-INF/lib";
144         LOG.error(error);
145         throw new RuntimeException(error);
146       } else {
147         if (LOG.isInfoEnabled()) {
148           LOG.info("Using default Theme {}", defaultTheme.getName());
149         }
150       }
151     }
152     if (!supportedThemeNames.isEmpty()) {
153       for (final String name : supportedThemeNames) {
154         final Theme theme = availableThemes.get(name);
155         checkThemeIsAvailable(name, theme);
156         supportedThemes.add(theme);
157         if (LOG.isDebugEnabled()) {
158           LOG.debug("name = '{}'", name);
159           LOG.debug("supportedThemes.last() = '{}'", supportedThemes.get(supportedThemes.size() - 1));
160         }
161       }
162     }
163   }
164 
165   private void checkThemeIsAvailable(final String name, final Theme theme) {
166     if (theme == null) {
167       final String error = "Theme not found! name: '" + name + "'. "
168           + "Please ensure you have a tobago-config.xml with a theme-definition in your "
169           + "theme JAR. Found the following themes: " + availableThemes.keySet();
170       LOG.error(error);
171       throw new RuntimeException(error);
172     }
173   }
174 
175   public Theme getTheme(final String name) {
176     if (name == null) {
177       LOG.debug("searching theme: null");
178       return defaultTheme;
179     }
180     if (defaultTheme.getName().equals(name)) {
181       return defaultTheme;
182     }
183     for (final Theme theme : supportedThemes) {
184       if (theme.getName().equals(name)) {
185         return theme;
186       }
187     }
188     LOG.debug("searching theme '{}' not found. Using default: {}", name, defaultTheme);
189     return defaultTheme;
190   }
191 
192   protected void setDefaultThemeName(final String defaultThemeName) {
193     checkLocked();
194     this.defaultThemeName = defaultThemeName;
195   }
196 
197   public List<Theme> getSupportedThemes() {
198     return supportedThemes;
199   }
200 
201   protected void addResourceDir(final String resourceDir) {
202     checkLocked();
203     if (!resourceDirs.contains(resourceDir)) {
204       if (LOG.isInfoEnabled()) {
205         LOG.info("adding resourceDir = '{}'", resourceDir);
206       }
207       resourceDirs.add(0, resourceDir);
208     }
209   }
210 
211   public List<String> getResourceDirs() {
212     return resourceDirs;
213   }
214 
215   public Theme getDefaultTheme() {
216     return defaultTheme;
217   }
218 
219   protected void setAvailableThemes(final Map<String, Theme> availableThemes) {
220     checkLocked();
221     this.availableThemes = availableThemes;
222     for (final Theme theme : this.availableThemes.values()) {
223       addResourceDir(theme.getResourcePath());
224     }
225   }
226 
227   protected RenderersConfig getRenderersConfig() {
228     return renderersConfig;
229   }
230 
231   protected void setRenderersConfig(final RenderersConfig renderersConfig) {
232     checkLocked();
233     this.renderersConfig = renderersConfig;
234   }
235 
236   public ProjectStage getProjectStage() {
237     return projectStage;
238   }
239 
240   // TODO one init method
241   protected void initProjectState(final ServletContext servletContext) {
242     checkLocked();
243     String stageName = null;
244     try {
245       final Context ctx = new InitialContext();
246       final Object obj = JndiUtils.getJndiProperty(ctx, "jsf", "ProjectStage");
247       if (obj != null) {
248         if (obj instanceof String) {
249           stageName = (String) obj;
250         } else {
251           LOG.warn("JNDI lookup for key {} should return a java.lang.String value",
252               ProjectStage.PROJECT_STAGE_JNDI_NAME);
253         }
254       }
255     } catch (final NamingException e) {
256       // ignore
257     }
258 
259     if (stageName == null) {
260       stageName = servletContext.getInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME);
261     }
262 
263     if (stageName == null) {
264       stageName = System.getProperty("org.apache.myfaces.PROJECT_STAGE");
265     }
266 
267     if (stageName != null) {
268       try {
269         projectStage = ProjectStage.valueOf(stageName);
270       } catch (final IllegalArgumentException e) {
271         LOG.error("Couldn't discover the current project stage", e);
272       }
273     }
274     if (projectStage == null) {
275       if (LOG.isInfoEnabled()) {
276         LOG.info("Couldn't discover the current project stage, using {}", ProjectStage.Production);
277       }
278       projectStage = ProjectStage.Production;
279     }
280   }
281 
282   protected synchronized void initDefaultValidatorInfo() {
283     if (defaultValidatorInfo != null) {
284       checkLocked();
285     }
286     final FacesContext facesContext = FacesContext.getCurrentInstance();
287     if (facesContext != null) {
288       try {
289         final Application application = facesContext.getApplication();
290         final Map<String, String> map = application.getDefaultValidatorInfo();
291         if (map.size() > 0) {
292           defaultValidatorInfo = Collections.unmodifiableMap(map);
293         } else {
294           defaultValidatorInfo = Collections.emptyMap();
295         }
296       } catch (final Exception e) {
297         LOG.error("Can't initialize default validators (this happens with some JBoss servers).");
298         defaultValidatorInfo = Collections.emptyMap();
299       }
300     }
301   }
302 
303   public boolean isCreateSessionSecret() {
304     return createSessionSecret;
305   }
306 
307   protected void setCreateSessionSecret(final boolean createSessionSecret) {
308     checkLocked();
309     this.createSessionSecret = createSessionSecret;
310   }
311 
312   public boolean isCheckSessionSecret() {
313     return checkSessionSecret;
314   }
315 
316   protected void setCheckSessionSecret(final boolean checkSessionSecret) {
317     checkLocked();
318     this.checkSessionSecret = checkSessionSecret;
319   }
320 
321 
322   public boolean isPreventFrameAttacks() {
323     return preventFrameAttacks;
324   }
325 
326   protected void setPreventFrameAttacks(final boolean preventFrameAttacks) {
327     checkLocked();
328     this.preventFrameAttacks = preventFrameAttacks;
329   }
330 
331   public ContentSecurityPolicy getContentSecurityPolicy() {
332     return contentSecurityPolicy;
333   }
334 
335   public boolean isSetNosniffHeader() {
336     return setNosniffHeader;
337   }
338 
339   protected void setSetNosniffHeader(final boolean setNosniffHeader) {
340     checkLocked();
341     this.setNosniffHeader = setNosniffHeader;
342   }
343 
344   public Map<String, String> getDefaultValidatorInfo() {
345     // TODO: if the startup hasn't found a FacesContext and Application, this may depend on the order of the listeners.
346     if (defaultValidatorInfo == null) {
347       initDefaultValidatorInfo();
348     }
349     return defaultValidatorInfo;
350   }
351 
352   public Sanitizer getSanitizer() {
353     return sanitizer;
354   }
355 
356   protected void setSanitizer(Sanitizer sanitizer) {
357     checkLocked();
358     this.sanitizer = sanitizer;
359   }
360 
361   public boolean isAutoAccessKeyFromLabel() {
362     return autoAccessKeyFromLabel;
363   }
364 
365   public void setAutoAccessKeyFromLabel(boolean autoAccessKeyFromLabel) {
366     checkLocked();
367     this.autoAccessKeyFromLabel = autoAccessKeyFromLabel;
368   }
369 
370   @Override
371   public Map<String, String> getMimeTypes() {
372     return mimeTypes;
373   }
374 
375   @Override
376   public boolean isClassicDateTimePicker() {
377     return classicDateTimePicker;
378   }
379 
380   public void setClassicDateTimePicker(boolean classicDateTimePicker) {
381     this.classicDateTimePicker = classicDateTimePicker;
382   }
383 
384   @Override
385   public String toString() {
386     final StringBuilder builder = new StringBuilder();
387     builder.append("TobagoConfigImpl{");
388     builder.append("\nsupportedThemes=[");
389     for (final Theme supportedTheme : supportedThemes) {
390       builder.append(supportedTheme.getName());
391       builder.append(", ");
392     }
393     builder.append("], \ndefaultTheme=");
394     builder.append(defaultTheme.getName());
395     builder.append(", \nresourceDirs=");
396     builder.append(resourceDirs);
397     builder.append(", \navailableThemes=");
398     builder.append(availableThemes.keySet());
399     builder.append(", \nprojectStage=");
400     builder.append(projectStage);
401     builder.append(", \ncreateSessionSecret=");
402     builder.append(createSessionSecret);
403     builder.append(", \ncheckSessionSecret=");
404     builder.append(checkSessionSecret);
405     builder.append(", \npreventFrameAttacks=");
406     builder.append(preventFrameAttacks);
407     builder.append(", \ncontentSecurityPolicy=");
408     builder.append(contentSecurityPolicy);
409     builder.append(", \nsetNosniffHeader=");
410     builder.append(setNosniffHeader);
411     builder.append(", \ndefaultValidatorInfo=");
412     builder.append(defaultValidatorInfo);
413     builder.append(", \nsanitizer=");
414     builder.append(sanitizer);
415     builder.append(", \nautoAccessKeyFromLabel=");
416     builder.append(autoAccessKeyFromLabel);
417     builder.append(", \n=classicDateTimePicker");
418     builder.append(classicDateTimePicker);
419     // to see only different (ignore alternative names for the same theme)
420     builder.append(", \nthemes=");
421     final Set<Theme> all = new HashSet<Theme>(availableThemes.values());
422     builder.append(all);
423     builder.append(", \nmimeTypes=");
424     builder.append(mimeTypes);
425     builder.append('}');
426     return builder.toString();
427   }
428 }