001package org.javasimon.source; 002 003import org.javasimon.Manager; 004import org.javasimon.Simon; 005 006import java.util.Map; 007import java.util.concurrent.ConcurrentHashMap; 008 009/** 010 * Monitor source playing the role of cache for delegate monitor source. 011 * 012 * @param <L> Location/invocation context 013 * @param <M> Simon type 014 * @param <K> Location key 015 * @author gquintana 016 */ 017public abstract class CachedMonitorSource<L, M extends Simon, K> implements MonitorSource<L, M> { 018 019 /** Real monitor source. */ 020 private final MonitorSource<L, M> delegate; 021 022 /** Monitor/location information. */ 023 private static class MonitorInformation { 024 private final boolean monitored; 025 private final String name; 026 027 public MonitorInformation(boolean monitored, Simon simon) { 028 this.monitored = monitored; 029 if (simon == null) { 030 name = null; 031 } else { 032 this.name = simon.getName(); 033 } 034 } 035 036 public boolean isMonitored() { 037 return monitored; 038 } 039 040 public String getName() { 041 return name; 042 } 043 044 public Simon getMonitor(Manager manager) { 045 if (name == null) { 046 return null; 047 } else { 048 return manager.getSimon(name); 049 } 050 } 051 } 052 053 /** Not monitored monitor information. */ 054 private static final MonitorInformation NULL_MONITOR_INFORMATION = new MonitorInformation(false, null); 055 056 /** Map location key → monitor information. */ 057 private final Map<K, MonitorInformation> monitorInformations = new ConcurrentHashMap<>(); 058 059 /** 060 * Constructor with real {@link MonitorSource}. 061 * 062 * @param delegate Delegate provider monitors for real 063 */ 064 public CachedMonitorSource(MonitorSource<L, M> delegate) { 065 this.delegate = delegate; 066 } 067 068 /** Get location for given location. */ 069 protected abstract K getLocationKey(L location); 070 071 /** 072 * Get monitor information for given location. 073 * First monitor information is looked up in cache. 074 * Then, when not found, delegate is called. 075 * 076 * @param location Location 077 * @return Monitor information 078 */ 079 private MonitorInformation getMonitorInformation(L location) { 080 final K monitorKey = getLocationKey(location); 081 MonitorInformation monitorInformation = monitorInformations.get(monitorKey); 082 if (monitorInformation == null) { 083 // Not found, let's call delegate 084 if (delegate.isMonitored(location)) { 085 monitorInformation = new MonitorInformation(true, delegate.getMonitor(location)); 086 } else { 087 monitorInformation = NULL_MONITOR_INFORMATION; 088 } 089 monitorInformations.put(monitorKey, monitorInformation); 090 } 091 return monitorInformation; 092 } 093 094 /** Remove monitor information for given location. */ 095 private void removeMonitorInformation(L location) { 096 monitorInformations.remove(getLocationKey(location)); 097 } 098 099 /** 100 * Check whether location should be monitored. 101 * Response is entirely based on cache 102 */ 103 @Override 104 public boolean isMonitored(L location) { 105 return getMonitorInformation(location).isMonitored(); 106 } 107 108 @SuppressWarnings("unchecked") 109 private M getMonitorOnce(L location) { 110 return (M) getMonitorInformation(location).getMonitor(getManager()); 111 } 112 113 /** 114 * Get Simon for the specified location. 115 * Simon is retrieved from name in cache. 116 * 117 * @param location Location 118 * @return Simon for the specified location 119 */ 120 @Override 121 public M getMonitor(L location) { 122 M monitor = getMonitorOnce(location); 123 // In case monitor was removed from manager, we retry 124 if (monitor == null) { 125 removeMonitorInformation(location); 126 monitor = getMonitorOnce(location); 127 } 128 return monitor; 129 } 130 131 @Override 132 public Manager getManager() { 133 return delegate.getManager(); 134 } 135}