sirius.kernel.di.PartRegistry Maven / Gradle / Ivy
Show all versions of sirius-kernel Show documentation
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.kernel.di;
import com.google.common.collect.Maps;
import sirius.kernel.Sirius;
import sirius.kernel.async.ExecutionPoint;
import sirius.kernel.commons.Explain;
import sirius.kernel.commons.MultiMap;
import sirius.kernel.commons.Strings;
import sirius.kernel.commons.Tuple;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
/**
* An instance of PartRegistry is kept by {@link sirius.kernel.di.Injector} to track all registered
* parts.
*/
class PartRegistry implements MutableGlobalContext {
/*
* Contains all registered parts
*/
private final MultiMap, Object> parts = MultiMap.createSynchronized();
/*
* Contains classes which are replaced by customer customizations (custom classes). This map is used
* to speed up checking while initializing the system.
*
* Content: ClassToReplace -> Replacement
*/
private final Map, Object> shadowMap = Maps.newHashMap();
/*
* Contains all registered parts with a unique name. These parts will also
* be contained in parts. This is just a lookup map if searched by unique
* name.
*/
private final Map, Map> namedParts = Maps.newConcurrentMap();
@SuppressWarnings("unchecked")
@Override
public P getPart(Class
clazz) {
Collection> items = parts.get(clazz);
if (items.isEmpty()) {
return null;
}
if (items.size() > 1) {
Injector.LOG.WARN("Retrieving a Part for %s from multiple implementations (%s) - picking a random one!"
+ " Use @Replace to clarify! Context: %s",
clazz.getName(),
items,
ExecutionPoint.snapshot().toString());
}
return (P) items.iterator().next();
}
@Override
public
PartCollection
getPartCollection(final Class
partInterface) {
return new PartCollection
() {
@Override
public Class
getInterface() {
return partInterface;
}
@Override
public Collection
getParts() {
return PartRegistry.this.getParts(partInterface);
}
@Override
public Iterator
iterator() {
return getParts().iterator();
}
};
}
@SuppressWarnings("unchecked")
@Override
public
Collection
getParts(Class extends P> partInterface) {
return (Collection
) parts.get(partInterface);
}
@SuppressWarnings("unchecked")
@Override
public Collection getParts(@Nonnull Class lookupClass, @Nonnull Class extends P> partType) {
return (Collection) parts.get(lookupClass);
}
@SuppressWarnings("unchecked")
@Override
public
Collection> getNamedParts(@Nonnull Class partInterface) {
return (Collection>) (Object) Tuple.fromMap(namedParts.get(partInterface));
}
@Override
public T wire(T object) {
// Wire....
Class> clazz = object.getClass();
while (clazz != null) {
wireClass(clazz, object);
clazz = clazz.getSuperclass();
}
return object;
}
/*
* Called to initialize all static fields with annotations
*/
void wireClass(Class> clazz) {
while (clazz != null) {
wireClass(clazz, null);
clazz = clazz.getSuperclass();
}
}
/*
* Called to initialize all field of the given class in the given object
*/
private void wireClass(Class> clazz, Object object) {
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isFinal(field.getModifiers()) && (object != null
|| Modifier.isStatic(field.getModifiers()))) {
field.setAccessible(true);
getParts(FieldAnnotationProcessor.class).stream()
.filter(p -> field.isAnnotationPresent(p.getTrigger()))
.forEach(p -> {
try {
p.handle(this, object, field);
} catch (Exception e) {
Injector.LOG.WARN(
"Cannot process annotation %s on %s.%s: %s "
+ "(%s)",
p.getTrigger().getName(),
field.getDeclaringClass().getName(),
field.getName(),
e.getMessage(),
e.getClass().getName());
}
});
}
}
}
@Override
public void registerPart(Object part, Class>... implementedInterfaces) {
String customizationName = Sirius.getCustomizationName(part.getClass().getName());
if (!Sirius.isActiveCustomization(customizationName)) {
return;
}
Object successor = shadowMap.get(part.getClass());
Class> predecessor = determinePredecessor(part, customizationName);
registerPart(part, implementedInterfaces, predecessor, successor);
}
private void registerPart(Object part, Class>[] implementedInterfaces, Class> predecessor, Object successor) {
for (Class> iFace : implementedInterfaces) {
if (successor == null) {
parts.put(iFace, part);
if (predecessor != null) {
synchronized (parts) {
parts.getUnderlyingMap().get(iFace).removeIf(p -> p.getClass().equals(predecessor));
}
}
} else {
synchronized (parts) {
Collection