View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.joran.spi;
15  
16  import java.util.ArrayList;
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  import java.util.function.Supplier;
21  
22  import ch.qos.logback.core.Context;
23  import ch.qos.logback.core.joran.action.Action;
24  import ch.qos.logback.core.spi.ContextAwareBase;
25  import ch.qos.logback.core.util.OptionHelper;
26  
27  /**
28   * This class implements the {@link RuleStore} interface. It is the rule store
29   * implementation used by default in Joran.
30   * 
31   * @author Ceki Gülcü
32   * 
33   */
34  public class SimpleRuleStore extends ContextAwareBase implements RuleStore {
35  
36      static String KLEENE_STAR = "*";
37  
38      // key: Pattern instance, value: ArrayList containing actions
39      HashMap<ElementSelector, Supplier<Action>> rules = new HashMap<>();
40  
41      List<String> transparentPathParts = new ArrayList<>(2);
42      Map<String, String> pathPartsMapForRenaming = new HashMap<>(2);
43  
44      public SimpleRuleStore(Context context) {
45          setContext(context);
46      }
47  
48  
49      public void addTransparentPathPart(String pathPart) {
50          if (pathPart == null)
51              throw new IllegalArgumentException("pathPart cannot be null");
52  
53          pathPart = pathPart.trim();
54  
55          if (pathPart.isEmpty())
56              throw new IllegalArgumentException("pathPart cannot be empty or to consist of only spaces");
57  
58          if (pathPart.contains("/"))
59              throw new IllegalArgumentException("pathPart cannot contain '/', i.e. the forward slash character");
60  
61          transparentPathParts.add(pathPart);
62  
63      }
64  
65      /**
66       * Rename path parts.
67       *
68       * @param originalName the name before renaming
69       * @param modifiedName the after renaming
70       * @since 1.5.5
71       */
72      @Override
73      public void addPathPathMapping(String originalName, String modifiedName) {
74          pathPartsMapForRenaming.put(originalName, modifiedName);
75      }
76  
77      /**
78       * Add a new rule, i.e. a pattern, action pair to the rule store.
79       * <p>
80       * Note that the added action's LoggerRepository will be set in the process.
81       */
82      public void addRule(ElementSelector elementSelector, Supplier<Action> actionSupplier) {
83  
84          Supplier<Action> existing = rules.get(elementSelector);
85  
86          if (existing == null) {
87              rules.put(elementSelector, actionSupplier);
88          } else {
89              throw new IllegalStateException(elementSelector.toString() + " already has an associated action supplier");
90          }
91      }
92  
93      public void addRule(ElementSelector elementSelector, String actionClassName) {
94          Action action = null;
95  
96          try {
97              action = (Action) OptionHelper.instantiateByClassName(actionClassName, Action.class, context);
98          } catch (Exception e) {
99              addError("Could not instantiate class [" + actionClassName + "]", e);
100         }
101         if (action != null) {
102      //       addRule(elementSelector, action);
103         }
104     }
105 
106     // exact match has the highest priority
107     // if no exact match, check for suffix (tail) match, i.e matches
108     // of type */x/y. Suffix match for */x/y has higher priority than match for
109     // */x
110     // if no suffix match, check for prefix match, i.e. matches for x/*
111     // match for x/y/* has higher priority than matches for x/*
112 
113     public Supplier<Action> matchActions(ElementPath elementPath) {
114         
115         Supplier<Action> actionSupplier = internalMatchAction(elementPath);
116         if(actionSupplier != null) {
117             return actionSupplier;
118         }
119 
120         actionSupplier = matchActionsWithoutTransparentParts(elementPath);
121         if(actionSupplier != null) {
122             return actionSupplier;
123         }
124 
125         return matchActionsWithRenamedParts(elementPath);
126 
127     }
128 
129     private Supplier<Action> matchActionsWithoutTransparentParts(ElementPath elementPath) {
130         ElementPath cleanedElementPath = removeTransparentPathParts(elementPath);
131         return internalMatchAction(cleanedElementPath);
132     }
133 
134     private Supplier<Action> matchActionsWithRenamedParts(ElementPath elementPath) {
135         ElementPath renamedElementPath = renamePathParts(elementPath);
136         return internalMatchAction(renamedElementPath);
137     }
138 
139     private Supplier<Action> internalMatchAction(ElementPath elementPath) {
140         Supplier<Action> actionSupplier;
141 
142         if ((actionSupplier = fullPathMatch(elementPath)) != null) {
143             return actionSupplier;
144         } else if ((actionSupplier = suffixMatch(elementPath)) != null) {
145             return actionSupplier;
146         } else if ((actionSupplier = prefixMatch(elementPath)) != null) {
147             return actionSupplier;
148         } else if ((actionSupplier = middleMatch(elementPath)) != null) {
149             return actionSupplier;
150         } else {
151             return null;
152         }
153     }
154 
155     ElementPath removeTransparentPathParts(ElementPath originalElementPath) {
156 
157         List<String> preservedElementList = new ArrayList<>(originalElementPath.partList.size());
158 
159         for (String part : originalElementPath.partList) {
160             boolean shouldKeep = transparentPathParts.stream().noneMatch(p -> p.equalsIgnoreCase(part));
161             if (shouldKeep)
162                 preservedElementList.add(part);
163         }
164 
165         return new ElementPath(preservedElementList);
166 
167     }
168 
169 
170     ElementPath renamePathParts(ElementPath originalElementPath) {
171 
172         List<String> result = new ArrayList<>(originalElementPath.partList.size());
173 
174         for (String part : originalElementPath.partList) {
175             String modifiedName = pathPartsMapForRenaming.getOrDefault(part, part);
176             result.add(modifiedName);
177         }
178 
179         return new ElementPath(result);
180     }
181 
182 
183     Supplier<Action> fullPathMatch(ElementPath elementPath) {
184         for (ElementSelector selector : rules.keySet()) {
185             if (selector.fullPathMatch(elementPath))
186                 return rules.get(selector);
187         }
188         return null;
189     }
190 
191     // Suffix matches are matches of type */x/y
192     Supplier<Action> suffixMatch(ElementPath elementPath) {
193         int max = 0;
194         ElementSelector longestMatchingElementSelector = null;
195 
196         for (ElementSelector selector : rules.keySet()) {
197             if (isSuffixPattern(selector)) {
198                 int r = selector.getTailMatchLength(elementPath);
199                 if (r > max) {
200                     max = r;
201                     longestMatchingElementSelector = selector;
202                 }
203             }
204         }
205 
206         if (longestMatchingElementSelector != null) {
207             return rules.get(longestMatchingElementSelector);
208         } else {
209             return null;
210         }
211     }
212 
213     private boolean isSuffixPattern(ElementSelector p) {
214         return (p.size() > 1) && p.get(0).equals(KLEENE_STAR);
215     }
216 
217     Supplier<Action> prefixMatch(ElementPath elementPath) {
218         int max = 0;
219         ElementSelector longestMatchingElementSelector = null;
220 
221         for (ElementSelector selector : rules.keySet()) {
222             String last = selector.peekLast();
223             if (isKleeneStar(last)) {
224                 int r = selector.getPrefixMatchLength(elementPath);
225                 // to qualify the match length must equal p's size omitting the '*'
226                 if ((r == selector.size() - 1) && (r > max)) {
227                     max = r;
228                     longestMatchingElementSelector = selector;
229                 }
230             }
231         }
232 
233         if (longestMatchingElementSelector != null) {
234             return rules.get(longestMatchingElementSelector);
235         } else {
236             return null;
237         }
238     }
239 
240     private boolean isKleeneStar(String last) {
241         return KLEENE_STAR.equals(last);
242     }
243 
244     Supplier<Action> middleMatch(ElementPath path) {
245 
246         int max = 0;
247         ElementSelector longestMatchingElementSelector = null;
248 
249         for (ElementSelector selector : rules.keySet()) {
250             String last = selector.peekLast();
251             String first = null;
252             if (selector.size() > 1) {
253                 first = selector.get(0);
254             }
255             if (isKleeneStar(last) && isKleeneStar(first)) {
256                 List<String> copyOfPartList = selector.getCopyOfPartList();
257                 if (copyOfPartList.size() > 2) {
258                     copyOfPartList.remove(0);
259                     copyOfPartList.remove(copyOfPartList.size() - 1);
260                 }
261 
262                 int r = 0;
263                 ElementSelector clone = new ElementSelector(copyOfPartList);
264                 if (clone.isContainedIn(path)) {
265                     r = clone.size();
266                 }
267                 if (r > max) {
268                     max = r;
269                     longestMatchingElementSelector = selector;
270                 }
271             }
272         }
273 
274         if (longestMatchingElementSelector != null) {
275             return rules.get(longestMatchingElementSelector);
276         } else {
277             return null;
278         }
279     }
280 
281     public String toString() {
282         final String TAB = "  ";
283 
284         StringBuilder retValue = new StringBuilder();
285 
286         retValue.append("SimpleRuleStore ( ").append("rules = ").append(this.rules).append(TAB).append(" )");
287 
288         return retValue.toString();
289     }
290 
291 }