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