com.speedment.common.injector.internal.dependency.DependencyGraphImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of injector Show documentation
Show all versions of injector Show documentation
A small dependency injection library.
/**
*
* Copyright (c) 2006-2019, Speedment, Inc. All Rights Reserved.
*
* 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 com.speedment.common.injector.internal.dependency;
import com.speedment.common.injector.State;
import com.speedment.common.injector.annotation.Execute;
import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.WithState;
import com.speedment.common.injector.dependency.Dependency;
import com.speedment.common.injector.dependency.DependencyGraph;
import com.speedment.common.injector.dependency.DependencyNode;
import com.speedment.common.injector.exception.CyclicReferenceException;
import com.speedment.common.injector.execution.Execution;
import com.speedment.common.injector.internal.InjectorImpl;
import com.speedment.common.injector.internal.execution.ReflectionExecutionImpl;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import static com.speedment.common.injector.internal.util.ReflectionUtil.traverseMethods;
import static java.util.stream.Collectors.joining;
/**
* The default implementation of the {@link DependencyGraph} interface.
*
* @author Emil Forslund
* @since 1.0.0
*/
public final class DependencyGraphImpl implements DependencyGraph {
private final Map, DependencyNode> nodes;
public static DependencyGraph create(Stream> injectables)
throws CyclicReferenceException {
final DependencyGraphImpl graph = new DependencyGraphImpl();
injectables.forEach(graph::getOrCreate);
graph.inject();
return graph;
}
private DependencyGraphImpl() {
nodes = new ConcurrentHashMap<>();
nodes.put(InjectorImpl.class, new InjectorDependencyNode());
}
@Override
public DependencyNode get(Class> clazz) throws IllegalArgumentException {
for (final Class> impl : nodes.keySet()) {
if (clazz.isAssignableFrom(impl)) {
return nodes.get(impl);
}
}
throw new IllegalArgumentException(
"There is no implementation of '" + clazz +
"' in the injection dependency graph."
);
}
@Override
public DependencyNode getOrCreate(Class> clazz) {
return nodes.computeIfAbsent(clazz, DependencyNodeImpl::new);
}
@Override
public DependencyGraph inject() {
nodes.forEach((clazz, node) -> {
// Go through all the methods with the '@Execute'-annotation in the
// class and add them to the executors set.
traverseMethods(clazz)
.filter(m -> m.isAnnotationPresent(Execute.class))
.forEach(m -> {
node.getExecutions().add(createExecution(m, State.STARTED));
});
// Go through all the methods with the '@ExecuteBefore'-annotation
// in the class and add them to the executors set.
traverseMethods(clazz)
.filter(m -> m.isAnnotationPresent(ExecuteBefore.class))
.forEach(m -> {
final ExecuteBefore execute =
m.getAnnotation(ExecuteBefore.class);
node.getExecutions()
.add(createExecution(m, execute.value()));
});
});
return this;
}
@Override
public Stream nodes() {
return nodes.values().stream();
}
private static String methodName(Method m) {
return m.getDeclaringClass().getName() + "#" +
m.getName() + "(" +
Stream.of(m.getParameterTypes())
.map(Class::getSimpleName)
.collect(joining(", ")) +
")";
}
private Execution> createExecution(Method m, State executeBefore) {
final Set dependencies = new HashSet<>();
try {
for (int i = 0; i < m.getParameterCount(); i++) {
final Parameter p = m.getParameters()[i];
final WithState ws = p.getAnnotation(WithState.class);
if (ws != null) {
final Class> type = p.getType();
final State state = ws.value();
try {
dependencies.add(
new DependencyImpl(get(type), state)
);
} catch (final CyclicReferenceException ex) {
throw new CyclicReferenceException(
m.getDeclaringClass(), ex
);
}
}
}
} catch (final CyclicReferenceException ex) {
throw new IllegalStateException(
"Could not execute method " + methodName(m) +
" since one of its dependencies had not been injected.",
ex
);
}
return new ReflectionExecutionImpl<>(
m.getDeclaringClass(),
executeBefore, dependencies, m
);
}
}