/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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 net.maritimecloud.internal.util;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;
import net.maritimecloud.util.Binary;
/**
*
* @author Kasper Nielsen
*/
public class MessageStore<T extends StoredMessage> {
static final int HASH_LENGTH = 32;
final long timeToLiveNanos = TimeUnit.NANOSECONDS.convert(1, TimeUnit.HOURS);
/** All messages, keyed by hash. Is a skip list to allow for prefix search. */
final ConcurrentSkipListMap<Binary, T> messages = new ConcurrentSkipListMap<>();
/** All messages ordered by time, uses hash for breaking ties. */
final ConcurrentSkipListMap<Key, T> messagesByTime = new ConcurrentSkipListMap<>();
public void addMessage(T m) {
messages.put(m.getMessageId(), m);
messagesByTime.put(new Key(m), m);
}
public T find(Binary key) {
return messages.get(key);
}
public Set<T> findPrefixed(Binary prefix) {
return Collections.emptySet();
}
public void forEach(Consumer<? super T> consumer) {
messages.values().forEach(consumer);
}
public void forEachEldestFirst(Consumer<? super T> consumer) {
messagesByTime.descendingMap().values().forEach(consumer);
}
public void clear() {
// It would be easier to just clear the two maps.
// But this would fail if we get a concurrent update
pruneMessagesOldThan(Long.MAX_VALUE);
}
public void pruneMessages(Predicate<? super T> predicate) {
for (Map.Entry<Key, T> e : messagesByTime.entrySet()) {
if (predicate.test(e.getValue())) {
messagesByTime.remove(e.getKey());
messages.remove(e.getValue().getMessageId());
}
}
}
public void pruneMessagesOldThan(long timestamp) {
pruneMessages(e -> e.getTimestamp() < timestamp);
}
static class Key implements Comparable<Key> {
final StoredMessage msg;
Key(StoredMessage msg) {
this.msg = msg;
}
/** {@inheritDoc} */
@Override
public int compareTo(Key o) {
if (msg.getTimestamp() == o.msg.getTimestamp()) {
return msg.getMessageId().compareTo(o.msg.getMessageId());
}
return Long.compare(msg.getTimestamp(), o.msg.getTimestamp());
}
}
}