Skip to content

Commit

Permalink
Refactor state machine to ensure protected operations can be properly…
Browse files Browse the repository at this point in the history
… detected and accessed.
  • Loading branch information
kuujo committed Jun 1, 2015
1 parent 3a299c7 commit 19cbed6
Showing 1 changed file with 92 additions and 36 deletions.
128 changes: 92 additions & 36 deletions raft/src/main/java/net/kuujo/copycat/raft/StateMachine.java
Expand Up @@ -43,33 +43,105 @@ protected StateMachine() {
* Initializes the state machine.
*/
private void init() {
init(getClass());

for (Method method : getClass().getMethods()) {
Filter filter = method.getAnnotation(Filter.class);
if (filter != null) {
if (method.getReturnType() != Boolean.class && method.getReturnType() != boolean.class) {
throw new ConfigurationException("filter method " + method + " must return boolean");
declareFilters(method);
declareOperations(method);
}
}

/**
* Initializes the state machine.
*/
private void init(Class<?> clazz) {
while (clazz != Object.class) {
for (Method method : clazz.getDeclaredMethods()) {
declareFilters(method);
declareOperations(method);
}

for (Class<?> iface : clazz.getInterfaces()) {
init(iface);
}
clazz = clazz.getSuperclass();
}
}

/**
* Declares any filters defined by the given method.
*/
private void declareFilters(Method method) {
Filter filter = method.getAnnotation(Filter.class);
if (filter != null) {
if (method.getReturnType() != Boolean.class && method.getReturnType() != boolean.class) {
throw new ConfigurationException("filter method " + method + " must return boolean");
}

method.setAccessible(true);
for (Class<? extends Command> command : filter.value()) {
if (command == Filter.All.class) {
allFilter = method;
} else if (!filters.containsKey(command)) {
filters.put(command, method);
}
}
}
}

for (Class<? extends Command> command : filter.value()) {
if (command == Filter.All.class) {
allFilter = method;
} else {
filters.put(command, method);
}
/**
* Finds the filter method for the given command.
*/
private Method findFilter(Class<? extends Command> type) {
Method method = filters.computeIfAbsent(type, t -> {
for (Map.Entry<Class<? extends Command>, Method> entry : filters.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
return entry.getValue();
}
}
return allFilter;
});

Apply apply = method.getAnnotation(Apply.class);
if (apply != null) {
for (Class<? extends Operation> command : apply.value()) {
if (command == Apply.All.class) {
allOperation = method;
} else {
operations.put(command, method);
}
if (method == null) {
throw new IllegalArgumentException("unknown command type: " + type);
}
return method;
}

/**
* Declares any operations defined by the given method.
*/
private void declareOperations(Method method) {
Apply apply = method.getAnnotation(Apply.class);
if (apply != null) {
method.setAccessible(true);
for (Class<? extends Operation> operation : apply.value()) {
if (operation == Apply.All.class) {
allOperation = method;
} else if (!operations.containsKey(operation)) {
operations.put(operation, method);
}
}
}
}

/**
* Finds the operation method for the given operation.
*/
private Method findOperation(Class<? extends Operation> type) {
Method method = operations.computeIfAbsent(type, t -> {
for (Map.Entry<Class<? extends Operation>, Method> entry : operations.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
return entry.getValue();
}
}
return allOperation;
});

if (method == null) {
throw new IllegalArgumentException("unknown operation type: " + type);
}
return method;
}

/**
Expand All @@ -89,16 +161,8 @@ public void register(Session session) {
* @return Whether to keep the commit.
*/
public boolean filter(Commit<? extends Command> commit, Compaction compaction) {
Method method = filters.get(commit.type());
if (method == null) {
method = allFilter;
if (method == null) {
throw new IllegalArgumentException("unknown command type: " + commit.type());
}
}

try {
return (boolean) method.invoke(this, commit);
return (boolean) findFilter(commit.type()).invoke(this, commit);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ApplicationException("failed to filter command", e);
}
Expand All @@ -111,16 +175,8 @@ public boolean filter(Commit<? extends Command> commit, Compaction compaction) {
* @return The operation result.
*/
public Object apply(Commit<? extends Operation> commit) {
Method method = operations.get(commit.type());
if (method == null) {
method = allOperation;
if (method == null) {
throw new IllegalArgumentException("unknown operation type:" + commit.type());
}
}

try {
return method.invoke(this, commit);
return findOperation(commit.type()).invoke(this, commit);
} catch (IllegalAccessException | InvocationTargetException e) {
return new ApplicationException("failed to invoke operation", e);
}
Expand Down

0 comments on commit 19cbed6

Please sign in to comment.