/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.actuate.metrics.util; import java.util.ArrayList; import java.util.NavigableMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; /** * Repository utility that stores stuff in memory with period-separated String keys. * * @param <T> the type to store * @author Dave Syer * @author Andy Wilkinson */ public class SimpleInMemoryRepository<T> { private ConcurrentNavigableMap<String, T> values = new ConcurrentSkipListMap<>(); private final ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>(); public T update(String name, Callback<T> callback) { Object lock = getLock(name); synchronized (lock) { T current = this.values.get(name); T value = callback.modify(current); this.values.put(name, value); return value; } } private Object getLock(String name) { Object lock = this.locks.get(name); if (lock == null) { Object newLock = new Object(); lock = this.locks.putIfAbsent(name, newLock); if (lock == null) { lock = newLock; } } return lock; } public void set(String name, T value) { this.values.put(name, value); } public long count() { return this.values.size(); } public void remove(String name) { this.values.remove(name); } public T findOne(String name) { return this.values.get(name); } public Iterable<T> findAll() { return new ArrayList<>(this.values.values()); } public Iterable<T> findAllWithPrefix(String prefix) { if (prefix.endsWith(".*")) { prefix = prefix.substring(0, prefix.length() - 1); } if (!prefix.endsWith(".")) { prefix = prefix + "."; } return new ArrayList<>( this.values.subMap(prefix, false, prefix + "~", true).values()); } public void setValues(ConcurrentNavigableMap<String, T> values) { this.values = values; } protected NavigableMap<String, T> getValues() { return this.values; } /** * Callback used to update a value. * * @param <T> the value type */ @FunctionalInterface public interface Callback<T> { /** * Modify an existing value. * @param current the value to modify * @return the updated value */ T modify(T current); } }