001package org.javasimon.callback; 002 003import org.javasimon.Counter; 004import org.javasimon.CounterSample; 005import org.javasimon.Manager; 006import org.javasimon.Simon; 007import org.javasimon.SimonPattern; 008import org.javasimon.Split; 009import org.javasimon.Stopwatch; 010import org.javasimon.StopwatchSample; 011 012import java.util.EnumMap; 013import java.util.List; 014import java.util.Map; 015import java.util.concurrent.CopyOnWriteArrayList; 016 017import javax.script.ScriptException; 018 019/** 020 * This callback combines Composite and Filter behavior. Filter can be configured 021 * via {@link #addRule(FilterRule.Type, String, String, Callback.Event...)} 022 * method and if the rule is satisfied the event is propagated to all 023 * children callbacks added via {@link #addCallback(Callback)}. XML facility for configuration 024 * is provided via {@link org.javasimon.ManagerConfiguration#readConfig(java.io.Reader)}. 025 * <p/> 026 * Filter without any rules does not propagate events (default DENY behavior). 027 * Any number of global rules (for {@link Callback.Event#ALL}) and per event rules can be added. 028 * Event rules have higher priority and if the filter passes on event rules, global rules are not consulted. 029 * Rules are checked in the order they were added to the filter. 030 * 031 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 032 * @see FilterRule 033 */ 034public final class CompositeFilterCallback implements FilterCallback, CompositeCallback { 035 036 private CompositeCallbackImpl callback = new CompositeCallbackImpl(); 037 038 private Map<Event, List<FilterRule>> rules; 039 040 /** Constructs composite filter callback. */ 041 public CompositeFilterCallback() { 042 rules = new EnumMap<>(Event.class); 043 for (Event event : Event.values()) { 044 rules.put(event, new CopyOnWriteArrayList<FilterRule>()); 045 } 046 } 047 048 @Override 049 public List<Callback> callbacks() { 050 return callback.callbacks(); 051 } 052 053 @Override 054 public void addCallback(Callback callback) { 055 this.callback.addCallback(callback); 056 } 057 058 @Override 059 public void removeCallback(Callback callback) { 060 this.callback.removeCallback(callback); 061 } 062 063 @Override 064 public void removeAllCallbacks() { 065 this.callback.removeAllCallbacks(); 066 } 067 068 @Override 069 public void initialize(Manager manager) { 070 callback.initialize(manager); 071 } 072 073 @Override 074 public void cleanup() { 075 callback.cleanup(); 076 } 077 078 @Override 079 public void onStopwatchAdd(Stopwatch stopwatch, Split split, StopwatchSample sample) { 080 if (rulesApplyTo(stopwatch, Event.STOPWATCH_ADD, split.runningFor())) { 081 callback.onStopwatchAdd(stopwatch, split, sample); 082 } 083 } 084 085 @Override 086 public void onStopwatchStart(Split split) { 087 Stopwatch stopwatch = split.getStopwatch(); 088 if (stopwatch != null && rulesApplyTo(stopwatch, Event.STOPWATCH_START, split)) { 089 callback.onStopwatchStart(split); 090 } 091 } 092 093 @Override 094 public void onStopwatchStop(Split split, StopwatchSample sample) { 095 Stopwatch stopwatch = split.getStopwatch(); 096 if (stopwatch != null && rulesApplyTo(stopwatch, Event.STOPWATCH_STOP, split)) { 097 callback.onStopwatchStop(split, sample); 098 } 099 } 100 101 @Override 102 public void onCounterDecrease(Counter counter, long dec, CounterSample sample) { 103 if (rulesApplyTo(counter, Event.COUNTER_DECREASE, dec)) { 104 callback.onCounterDecrease(counter, dec, sample); 105 } 106 } 107 108 @Override 109 public void onCounterIncrease(Counter counter, long inc, CounterSample sample) { 110 if (rulesApplyTo(counter, Event.COUNTER_INCREASE, inc)) { 111 callback.onCounterIncrease(counter, inc, sample); 112 } 113 } 114 115 @Override 116 public void onCounterSet(Counter counter, long val, CounterSample sample) { 117 if (rulesApplyTo(counter, Event.COUNTER_SET, val)) { 118 callback.onCounterSet(counter, val, sample); 119 } 120 } 121 122 @Override 123 public void onSimonCreated(Simon simon) { 124 if (rulesApplyTo(simon, Event.CREATED)) { 125 callback.onSimonCreated(simon); 126 } 127 } 128 129 @Override 130 public void onSimonDestroyed(Simon simon) { 131 if (rulesApplyTo(simon, Event.DESTROYED)) { 132 callback.onSimonDestroyed(simon); 133 } 134 } 135 136 @Override 137 public void onManagerClear() { 138 if (rulesApplyTo(null, Event.MANAGER_CLEAR)) { 139 callback.onManagerClear(); 140 } 141 } 142 143 @Override 144 public void onManagerMessage(String message) { 145 if (rulesApplyTo(null, Event.MESSAGE, message)) { 146 callback.onManagerMessage(message); 147 } 148 } 149 150 @Override 151 public void onManagerWarning(String warning, Exception cause) { 152 if (rulesApplyTo(null, Event.WARNING, cause)) { 153 callback.onManagerWarning(warning, cause); 154 } 155 } 156 157 @Override 158 public void addRule(FilterRule.Type type, String condition, String pattern, Event... events) { 159 SimonPattern simonPattern = SimonPattern.create(pattern); 160 FilterRule rule = new FilterRule(type, condition, simonPattern); 161 for (Event event : events) { 162 if (event != null) { 163 rules.get(event).add(rule); 164 } 165 } 166 if (events.length == 0) { 167 rules.get(Event.ALL).add(rule); 168 } 169 } 170 171 private boolean rulesApplyTo(Simon simon, Event checkedEvent, Object... params) { 172 // only if event rules are empty, check rules for ALL as a fallback 173 if (rules.get(checkedEvent).size() == 0) { 174 return checkRules(simon, Event.ALL, params); 175 } 176 return checkRules(simon, checkedEvent, params); 177 } 178 179 private boolean checkRules(Simon simon, Event event, Object... params) { 180 List<FilterRule> rulesForEvent = rules.get(event); 181 if (rulesForEvent.size() == 0) { // empty rule list => DENY 182 return false; 183 } 184 boolean allMustSatisfied = false; 185 for (FilterRule rule : rulesForEvent) { 186 boolean result = false; 187 try { 188 result = patternAndConditionCheck(simon, rule, params); 189 } catch (ScriptException e) { 190 onManagerWarning("Script exception while evaluating rule expression", e); 191 } 192 193 if (!result && rule.getType().equals(FilterRule.Type.MUST)) { // fast fail on MUST condition 194 return false; 195 } else if (result && rule.getType().equals(FilterRule.Type.MUST)) { // MUST condition met, let's go on 196 allMustSatisfied = true; 197 } else if (result && rule.getType().equals(FilterRule.Type.MUST_NOT)) { // fast fail on MUST NOT condition 198 return false; 199 } else if (!result && rule.getType().equals(FilterRule.Type.MUST_NOT)) { // MUST NOT condition met, go on 200 allMustSatisfied = true; 201 } else if (result && rule.getType().equals(FilterRule.Type.SUFFICE)) { // fast success on SUFFICE condition 202 return true; 203 } 204 } 205 return allMustSatisfied; 206 } 207 208 private boolean patternAndConditionCheck(Simon simon, FilterRule rule, Object... params) throws ScriptException { 209 //noinspection SimplifiableIfStatement 210 if (simon != null && rule.getPattern() != null && !rule.getPattern().accept(simon)) { 211 return false; 212 } 213 return rule.checkCondition(simon, params); 214 } 215}