001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.monitoring.store;
018    
019    import org.apache.commons.monitoring.Role;
020    import org.apache.commons.monitoring.configuration.Configuration;
021    import org.apache.commons.monitoring.counters.Counter;
022    import org.apache.commons.monitoring.counters.DefaultCounter;
023    import org.apache.commons.monitoring.gauges.Gauge;
024    
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.Map;
028    import java.util.TreeMap;
029    import java.util.concurrent.ConcurrentHashMap;
030    import java.util.concurrent.ConcurrentMap;
031    import java.util.concurrent.ConcurrentSkipListMap;
032    import java.util.concurrent.locks.Lock;
033    
034    public class DefaultDataStore implements DataStore {
035        private final ConcurrentMap<Counter.Key, Counter> counters = new ConcurrentHashMap<Counter.Key, Counter>(50);
036        private final Map<Role, Map<Long, Double>> gauges = new ConcurrentHashMap<Role, Map<Long, Double>>();
037    
038        @Override
039        public Counter getOrCreateCounter(final Counter.Key key) {
040            Counter counter = counters.get(key);
041            if (counter == null) {
042                counter = new DefaultCounter(key, this);
043                final Counter previous = counters.putIfAbsent(key, counter);
044                if (previous != null) {
045                    counter = previous;
046                }
047            }
048            return counter;
049        }
050    
051        @Override
052        public void clearCounters() {
053            counters.clear();
054        }
055    
056        @Override
057        public Collection<Counter> getCounters() {
058            return counters.values();
059        }
060    
061        @Override
062        public void addToCounter(final Counter counter, final double delta) {
063            if (!DefaultCounter.class.isInstance(counter)) {
064                throw new IllegalArgumentException(DefaultDataStore.class.getName() + " only supports " + DefaultCounter.class.getName());
065            }
066    
067            final DefaultCounter defaultCounter = DefaultCounter.class.cast(counter);
068            final Lock lock = defaultCounter.getLock();
069            lock.lock();
070            try {
071                defaultCounter.addInternal(delta);
072            } finally {
073                lock.unlock();
074            }
075        }
076    
077        @Override
078        public Map<Long, Double> getGaugeValues(GaugeValuesRequest gaugeValuesRequest) {
079            final Map<Long, Double> map = gauges.get(gaugeValuesRequest.getRole());
080            if (map == null) {
081                return Collections.emptyMap();
082            }
083    
084            final Map<Long, Double> copy = new TreeMap<Long, Double>( map );
085    
086            final Map<Long, Double> out = new TreeMap<Long, Double>();
087            for (final Map.Entry<Long, Double> entry : copy.entrySet()) {
088                final long time = entry.getKey();
089                if (time >= gaugeValuesRequest.getStart() && time <= gaugeValuesRequest.getEnd()) {
090                    out.put(time, entry.getValue());
091                }
092            }
093            return out;
094        }
095    
096        @Override
097        public void createOrNoopGauge(final Role role) {
098            gauges.put(role, new FixedSizedMap());
099        }
100    
101        @Override
102        public void addToGauge(final Gauge gauge, final long time, final double value) {
103            gauges.get(gauge.role()).put(time, value);
104        }
105    
106        // no perf issues here normally since add is called not that often
107        protected static class FixedSizedMap extends ConcurrentSkipListMap<Long, Double> {
108            private static final int MAX_SIZE = Configuration.getInteger(Configuration.COMMONS_MONITORING_PREFIX + "gauge.max-size", 100);
109    
110            protected FixedSizedMap() {
111                // no-op
112            }
113    
114            protected FixedSizedMap(final Map<Long, Double> value) {
115                super(value);
116            }
117    
118            @Override
119            public Double put(final Long key, final Double value) {
120                if (size() >= MAX_SIZE) {
121                    remove(keySet().iterator().next());
122                }
123                return super.put(key, value);
124            }
125        }
126    }