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.sanitizer.IgnoringSanitizer;
23  import org.apache.myfaces.tobago.sanitizer.JsoupSanitizer;
24  import org.apache.myfaces.tobago.sanitizer.Sanitizer;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Properties;
34  
35  public class TobagoConfigSorter implements Comparator<TobagoConfigFragment> {
36  
37    private static final Logger LOG = LoggerFactory.getLogger(TobagoConfigSorter.class);
38  
39    private List<TobagoConfigFragment> list;
40    private List<Pair> pairs;
41  
42    public TobagoConfigSorter(final List<TobagoConfigFragment> list) {
43      this.list = list;
44    }
45  
46    public void sort() {
47  
48      createRelevantPairs();
49  
50      makeTransitive();
51  
52      ensureIrreflexive();
53  
54      ensureAntiSymmetric();
55  
56      sort0();
57  
58      if (LOG.isInfoEnabled()) {
59        LOG.info("Order of the Tobago config files:");
60        for (final TobagoConfigFragment fragment : list) {
61          String name = fragment.getName();
62          if (name == null) {
63            name = "<unnamed>";
64          } else {
65            name = "'" + name + "'";
66          }
67          LOG.info("name=" + name + " url='" + fragment.getUrl() + "'");
68        }
69      }
70    }
71  
72    public TobagoConfigImpl merge() {
73  
74      final TobagoConfigImpl result = new TobagoConfigImpl();
75  
76      // default sanitizer
77      String sanitizerClass = JsoupSanitizer.class.getName();
78      Properties sanitizerProperties = new Properties();
79      sanitizerProperties.setProperty("whitelist", "relaxed");
80  
81      for (final TobagoConfigFragment fragment : list) {
82        // default theme
83        final String defaultTheme = fragment.getDefaultThemeName();
84        if (defaultTheme != null) {
85          result.setDefaultThemeName(defaultTheme);
86        }
87  
88        // supported themes
89        for (final String supported : fragment.getSupportedThemeNames()) {
90          result.addSupportedThemeName(supported);
91        }
92  
93        // resource dirs
94        for (final String dir : fragment.getResourceDirs()) {
95          result.addResourceDir(dir);
96        }
97  
98        // renderers config
99        if (fragment.getRenderersConfig() != null) {
100         if (result.getRenderersConfig() instanceof RenderersConfigImpl) {
101           ((RenderersConfigImpl) result.getRenderersConfig()).merge(fragment.getRenderersConfig(), false);
102         } else if (result.getRenderersConfig() == null) {
103           result.setRenderersConfig(fragment.getRenderersConfig());
104         }
105       }
106 
107       // session secret
108       if (fragment.getCreateSessionSecret() != null) {
109         result.setCreateSessionSecret(fragment.getCreateSessionSecret());
110       }
111       if (fragment.getCheckSessionSecret() != null) {
112         result.setCheckSessionSecret(fragment.getCheckSessionSecret());
113       }
114 
115       if (fragment.getPreventFrameAttacks() != null) {
116         result.setPreventFrameAttacks(fragment.getPreventFrameAttacks());
117       }
118 
119       if (fragment.getContentSecurityPolicy() != null) {
120         result.getContentSecurityPolicy().merge(fragment.getContentSecurityPolicy());
121       }
122 
123       if (fragment.getSetNosniffHeader() != null) {
124         result.setSetNosniffHeader(fragment.getSetNosniffHeader());
125       }
126 
127       if (fragment.getSanitizerClass() != null) {
128         sanitizerClass = fragment.getSanitizerClass();
129         sanitizerProperties = fragment.getSanitizerProperties();
130       }
131 
132       if (fragment.getAutoAccessKeyFromLabel() != null) {
133         result.setAutoAccessKeyFromLabel(fragment.getAutoAccessKeyFromLabel());
134       }
135 
136       if (fragment.getClassicDateTimePicker() != null) {
137         result.setClassicDateTimePicker(fragment.getClassicDateTimePicker());
138       }
139 
140       // theme definition
141       // todo
142 /*
143       for (Theme theme : fragment.getThemeDefinitions()) {
144         result.addThemeDefinition(theme);
145       }
146 */
147 
148       // url
149       // todo???
150 
151       final Map<String, String> mimeTypes = result.getMimeTypes();
152       for (final Map.Entry<String, String> entry : fragment.getMimeTypes().entrySet()) {
153         mimeTypes.put(entry.getKey(), entry.getValue());
154       }
155 
156     }
157 
158     if (sanitizerClass != null) {
159       try {
160         final Class<? extends Sanitizer> aClass = Class.forName(sanitizerClass).asSubclass(Sanitizer.class);
161         final Sanitizer sanitizer = aClass.newInstance();
162         sanitizer.setProperties(sanitizerProperties);
163         result.setSanitizer(sanitizer);
164       } catch (Throwable e) {
165         LOG.error("Can't create sanitizer: '" + sanitizerClass + "'", e);
166         result.setSanitizer(new IgnoringSanitizer());
167       }
168     }
169 
170     return result;
171   }
172 
173   protected void makeTransitive() {
174     // make the half order transitive: a < b && b < c => a < c
175     boolean growing = true;
176     while (growing) {
177       growing = false;
178       for (int i = 0; i < pairs.size(); i++) {
179         for (int j = 0; j < pairs.size(); j++) {
180           if (pairs.get(i).getHigher() == pairs.get(j).getLower()
181               && !isInRelation(pairs.get(i).getLower(), pairs.get(j).getHigher())) {
182             pairs.add(new Pair(pairs.get(i).getLower(), pairs.get(j).getHigher()));
183             growing = true;
184           }
185         }
186       }
187     }
188   }
189 
190   protected void ensureIrreflexive() {
191     for (final Pair a : pairs) {
192         if (a.getLower() == a.getHigher()) {
193           final StringBuffer buffer = new StringBuffer();
194           buffer.append("Ordering problem. There are conflicting order rules. Not irreflexive. " + "'");
195           buffer.append(a.getLower());
196           buffer.append("' < '");
197           buffer.append(a.getHigher());
198           buffer.append("'!\nThe reason may be a cycle.\n");
199           buffer.append("Complete list of rules: \n");
200           for (final Pair pair : pairs) {
201             buffer.append("'");
202             buffer.append(pair.getLower());
203             buffer.append("' < '");
204             buffer.append(pair.getHigher());
205             buffer.append("'\n");
206 
207           }
208           throw new RuntimeException(buffer.toString());
209         }
210       }
211   }
212 
213   protected void ensureAntiSymmetric() {
214     for (final Pair a : pairs) {
215       for (final Pair b : pairs) {
216         if (a.getLower() == b.getHigher() && a.getHigher() == b.getLower()) {
217           final StringBuffer buffer = new StringBuffer();
218           buffer.append("Ordering problem. There are conflicting order rules. Not antisymmetric. " + "'");
219           buffer.append(a.getLower());
220           buffer.append("' < '");
221           buffer.append(a.getHigher());
222           buffer.append("'" + "'");
223           buffer.append(a.getLower());
224           buffer.append("' > '");
225           buffer.append(a.getHigher());
226           buffer.append("'!\nThe reason may be a cycle.\n");
227           buffer.append("Complete list of rules: \n");
228           for (final Pair pair : pairs) {
229             buffer.append("'");
230             buffer.append(pair.getLower());
231             buffer.append("' < '");
232             buffer.append(pair.getHigher());
233             buffer.append("'\n");
234 
235           }
236           throw new RuntimeException(buffer.toString());
237         }
238       }
239     }
240   }
241 
242   public int compare(final TobagoConfigFragment a, final TobagoConfigFragment b) {
243     if (isInRelation(a, b)) {
244       return -1;
245     }
246     if (isInRelation(b, a)) {
247       return 1;
248     }
249     return 0;
250   }
251 
252   protected void createRelevantPairs() {
253 
254     pairs = new ArrayList<Pair>();
255 
256     // collecting all relations, which are relevant for us. We don't need "before" and "after" of unknown names.
257     for (final TobagoConfigFragment tobagoConfig : list) {
258       for (final String befores : tobagoConfig.getBefore()) {
259         final TobagoConfigFragment before = findByName(befores);
260         if (before != null) {
261           pairs.add(new Pair(tobagoConfig, before));
262         }
263       }
264       for (final String afters : tobagoConfig.getAfter()) {
265         final TobagoConfigFragment after = findByName(afters);
266         if (after != null) {
267           pairs.add(new Pair(after, tobagoConfig));
268         }
269       }
270     }
271   }
272 
273   protected void sort0() {
274     Collections.sort(list, this);
275   }
276 
277   private boolean isInRelation(final TobagoConfigFragment lower, final TobagoConfigFragment higher) {
278     for (final Pair p : pairs) {
279       if (p.getLower() == lower && p.getHigher() == higher) {
280         return true;
281       }
282     }
283     return false;
284   }
285 
286   private TobagoConfigFragment findByName(final String name) {
287     for (final TobagoConfigFragment tobagoConfig : list) {
288       if (name.equals(tobagoConfig.getName())) {
289         return tobagoConfig;
290       }
291     }
292     return null;
293   }
294 
295   protected List<Pair> getPairs() {
296     return pairs;
297   }
298 
299   private static class Pair {
300 
301     private final TobagoConfigFragment lower;
302     private final TobagoConfigFragment higher;
303 
304     private Pair(final TobagoConfigFragment lower, final TobagoConfigFragment higher) {
305       this.lower = lower;
306       this.higher = higher;
307     }
308 
309     public TobagoConfigFragment getLower() {
310       return lower;
311     }
312 
313     public TobagoConfigFragment getHigher() {
314       return higher;
315     }
316 
317     @Override
318     public String toString() {
319       return lower + "<" + higher;
320     }
321   }
322 
323 }