/*
* Copyright 2011-2014 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 pl.com.bottega.cqrs.command.impl;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import pl.com.bottega.cqrs.annotations.Command;
/**
* Manages command execution history based on {@link Command} annotation attributes<br>
* Commands that are annotated as unique=true are stored in this history.<br>
* History checks if the same command (equals) is called again.<br>
* <br>
* Each command class has it's own entries in history - history length can be parameterized via constructor parameter.
*
* @author Slawek
*
*/
class GateHistory {
@SuppressWarnings("serial")
// TODO Sprawdzic czy nie musi byc concurrent (history jest, na tym
// poziomie nie musi byc totalnej synchronizacji, to tylko rodzaj
// cache)
private class CommandExecutionsMap extends LinkedHashMap<Object, Date> {
protected boolean removeEldestEntry(Map.Entry<Object, Date> eldest) {
return this.size() > maxHistoryCapacity;
}
};
private static final int DEFAULT_MAX_HISTORY_CAPACITY = 3;
/**
* History model. Each command class has map of executions (command instance
* and time)
*/
@SuppressWarnings("rawtypes")
private Map<Class, CommandExecutionsMap> history = new ConcurrentHashMap<Class, CommandExecutionsMap>();
private int maxHistoryCapacity;
public GateHistory(int maxHistoryCapacity) {
this.maxHistoryCapacity = maxHistoryCapacity;
}
public GateHistory() {
this(DEFAULT_MAX_HISTORY_CAPACITY);
}
/**
*
* @param command
* @return true if command is not a repetition, false if command is
* repetition and should not be executed now
*/
public boolean register(Object command) {
if (!isUnique(command))
return true;
Date lastRun = getFromHistory(command);
// update history
Date now = new Date();
addToHistory(command, now);
// first run, so go
if (lastRun == null)
return true;
long uniqueStorageTimeout = getUniqueStorageTimeout(command);
// no timeout so by default it is duplicated
if (uniqueStorageTimeout == 0)
return false;
long milisFromLastRun = now.getTime() - lastRun.getTime();
return milisFromLastRun > uniqueStorageTimeout;
}
private boolean isUnique(Object command) {
if (!command.getClass().isAnnotationPresent(Command.class))
return false;
Command commandAnnotation = command.getClass().getAnnotation(
Command.class);
return commandAnnotation.unique();
}
private Long getUniqueStorageTimeout(Object command) {
Command commandAnnotation = command.getClass().getAnnotation(
Command.class);
return commandAnnotation.uniqueStorageTimeout();
}
private Date getFromHistory(Object command) {
Map<Object, Date> executions = history.get(command.getClass());
if (executions == null)
return null;
return executions.get(command);
}
private void addToHistory(Object command, Date executeDate) {
CommandExecutionsMap executions = history.get(command.getClass());
if (executions == null) {
executions = new CommandExecutionsMap();
history.put(command.getClass(), executions);
}
executions.put(command, executeDate);
}
}