001package org.javasimon; 002 003import org.javasimon.callback.Callback; 004import org.javasimon.callback.CompositeCallback; 005import org.javasimon.callback.CompositeCallbackImpl; 006import org.javasimon.clock.SimonClock; 007import org.javasimon.utils.SimonUtils; 008 009import java.lang.reflect.Constructor; 010import java.lang.reflect.InvocationTargetException; 011import java.util.ArrayList; 012import java.util.Collection; 013import java.util.Collections; 014import java.util.Map; 015import java.util.concurrent.ConcurrentHashMap; 016 017/** 018 * Implements fully functional {@link Manager} in the enabled state. Does not support 019 * {@link #enable()}/{@link #disable()} - for this use {@link SwitchingManager}. 020 * 021 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 022 */ 023public final class EnabledManager implements Manager { 024 025 private UnknownSimon rootSimon; 026 027 private final Map<String, AbstractSimon> allSimons = new ConcurrentHashMap<>(); 028 029 private final CompositeCallback callback = new CompositeCallbackImpl(); 030 031 private final ManagerConfiguration configuration; 032 033 private final SimonClock clock; 034 035 /** Creates new enabled manager. */ 036 public EnabledManager() { 037 this(SimonClock.SYSTEM); 038 } 039 040 public EnabledManager(SimonClock clock) { 041 this.clock = clock; 042 rootSimon = new UnknownSimon(ROOT_SIMON_NAME, this); 043 allSimons.put(ROOT_SIMON_NAME, rootSimon); 044 configuration = new ManagerConfiguration(this); 045 callback.initialize(this); 046 } 047 048 @Override 049 public Simon getSimon(String name) { 050 return allSimons.get(name); 051 } 052 053 @Override 054 public synchronized void destroySimon(String name) { 055 if (name.equals(ROOT_SIMON_NAME)) { 056 throw new SimonException("Root Simon cannot be destroyed!"); 057 } 058 AbstractSimon simon = allSimons.remove(name); 059 if (simon.getChildren().size() > 0) { 060 replaceUnknownSimon(simon, UnknownSimon.class); 061 } else { 062 ((AbstractSimon) simon.getParent()).replaceChild(simon, null); 063 } 064 callback.onSimonDestroyed(simon); 065 } 066 067 @Override 068 public synchronized void clear() { 069 allSimons.clear(); 070 rootSimon = new UnknownSimon(ROOT_SIMON_NAME, this); 071 allSimons.put(ROOT_SIMON_NAME, rootSimon); 072 callback.onManagerClear(); 073 } 074 075 @Override 076 public Counter getCounter(String name) { 077 return (Counter) getOrCreateSimon(name, CounterImpl.class); 078 } 079 080 @Override 081 public Stopwatch getStopwatch(String name) { 082 return (Stopwatch) getOrCreateSimon(name, StopwatchImpl.class); 083 } 084 085 @Override 086 public Simon getRootSimon() { 087 return rootSimon; 088 } 089 090 @Override 091 public Collection<String> getSimonNames() { 092 return Collections.unmodifiableCollection(allSimons.keySet()); 093 } 094 095 @SuppressWarnings({"unchecked"}) 096 @Override 097 public Collection<Simon> getSimons(SimonFilter simonFilter) { 098 if (simonFilter == null) { 099 return Collections.unmodifiableCollection((Collection) allSimons.values()); 100 } 101 Collection<Simon> simons = new ArrayList<>(); 102 for (AbstractSimon simon : allSimons.values()) { 103 if (simonFilter.accept(simon)) { 104 simons.add(simon); 105 } 106 } 107 return simons; 108 } 109 110 private Simon getOrCreateSimon(String name, Class<? extends AbstractSimon> simonClass) { 111 if (name == null) { 112 // create an "anonymous" Simon - Manager does not care about it anymore 113 return instantiateSimon(null, simonClass); 114 } 115 if (name.equals(ROOT_SIMON_NAME)) { 116 throw new SimonException("Root Simon cannot be replaced or recreated!"); 117 } 118 AbstractSimon simon = allSimons.get(name); 119 if (simon != null && simonClass.isInstance(simon)) { 120 return simon; 121 } else if (simon != null && !(simon instanceof UnknownSimon)) { 122 throw new SimonException("Simon named '" + name + "' already exists and its type is '" + 123 simon.getClass().getName() + "' while requested type is '" + simonClass.getName() + "'."); 124 } else { 125 return createSimon(name, simonClass); 126 } 127 } 128 129 /** 130 * Even with ConcurrentHashMap we want to synchronize here, so newly created Simons can be fully 131 * set up with {@link Callback#onSimonCreated(Simon)}. ConcurrentHashMap still works fine for 132 * listing Simons, etc. 133 */ 134 private synchronized Simon createSimon(String name, Class<? extends AbstractSimon> simonClass) { 135 AbstractSimon simon = allSimons.get(name); 136 if (simon == null) { 137 SimonUtils.validateSimonName(name); 138 simon = newSimon(name, simonClass); 139 } else if (simon instanceof UnknownSimon) { 140 simon = replaceUnknownSimon(simon, simonClass); 141 } 142 callback.onSimonCreated(simon); 143 return simon; 144 } 145 146 // called from synchronized method 147 private AbstractSimon replaceUnknownSimon(AbstractSimon simon, Class<? extends AbstractSimon> simonClass) { 148 AbstractSimon newSimon = instantiateSimon(simon.getName(), simonClass); 149 newSimon.enabled = simon.enabled; 150 151 // fixes parent link and parent's children list 152 ((AbstractSimon) simon.getParent()).replaceChild(simon, newSimon); 153 154 // fixes children list and all children's parent link 155 for (Simon child : simon.getChildren()) { 156 newSimon.addChild((AbstractSimon) child); 157 ((AbstractSimon) child).setParent(newSimon); 158 } 159 160 allSimons.put(simon.getName(), newSimon); 161 return newSimon; 162 } 163 164 // called from synchronized method 165 private AbstractSimon newSimon(String name, Class<? extends AbstractSimon> simonClass) { 166 AbstractSimon simon = instantiateSimon(name, simonClass); 167 if (name != null) { 168 addToHierarchy(simon, name); 169 SimonConfiguration config = configuration.getConfig(name); 170 if (config.getState() != null) { 171 simon.setState(config.getState(), false); 172 } 173 allSimons.put(name, simon); 174 } 175 return simon; 176 } 177 178 private AbstractSimon instantiateSimon(String name, Class<? extends AbstractSimon> simonClass) { 179 AbstractSimon simon; 180 try { 181 Constructor<? extends AbstractSimon> constructor = simonClass.getDeclaredConstructor(String.class, Manager.class); 182 simon = constructor.newInstance(name, this); 183 } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) { 184 throw new SimonException(e); 185 } 186 return simon; 187 } 188 189 private void addToHierarchy(AbstractSimon simon, String name) { 190 int ix = name.lastIndexOf(HIERARCHY_DELIMITER); 191 AbstractSimon parent = rootSimon; 192 if (ix != -1) { 193 String parentName = name.substring(0, ix); 194 parent = allSimons.get(parentName); 195 if (parent == null) { 196 parent = new UnknownSimon(parentName, this); 197 addToHierarchy(parent, parentName); 198 allSimons.put(parentName, parent); 199 } 200 } 201 parent.addChild(simon); 202 } 203 204 @Override 205 public CompositeCallback callback() { 206 return callback; 207 } 208 209 @Override 210 public ManagerConfiguration configuration() { 211 return configuration; 212 } 213 214 /** Throws {@link UnsupportedOperationException}. */ 215 @Override 216 public void enable() { 217 throw new UnsupportedOperationException("Only SwitchingManager supports this operation."); 218 } 219 220 /** Throws {@link UnsupportedOperationException}. */ 221 @Override 222 public void disable() { 223 throw new UnsupportedOperationException("Only SwitchingManager supports this operation."); 224 } 225 226 /** 227 * Returns true. 228 * 229 * @return true 230 */ 231 @Override 232 public boolean isEnabled() { 233 return true; 234 } 235 236 @Override 237 public void message(String message) { 238 callback.onManagerMessage(message); 239 } 240 241 @Override 242 public void warning(String warning, Exception cause) { 243 callback.onManagerWarning(warning, cause); 244 } 245 246 @Override 247 public long nanoTime() { 248 return clock.nanoTime(); 249 } 250 251 @Override 252 public long milliTime() { 253 return clock.milliTime(); 254 } 255 256 @Override 257 public long millisForNano(long nanos) { 258 return clock.millisForNano(nanos); 259 } 260 261 synchronized void purgeIncrementalSimonsOlderThan(long thresholdMs) { 262 for (Simon simon : allSimons.values()) { 263 if (simon instanceof AbstractSimon) { 264 AbstractSimon abstractSimon = (AbstractSimon) simon; 265 abstractSimon.purgeIncrementalSimonsOlderThan(thresholdMs); 266 } 267 } 268 } 269}