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 &rarr; 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}