/*
* 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);
}
}