com.coditory.quark.context.Context Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quark-context Show documentation
Show all versions of quark-context Show documentation
Coditory Quark Configuration Library
package com.coditory.quark.context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.coditory.quark.context.Args.checkNonNull;
import static com.coditory.quark.context.BeanDescriptor.descriptor;
import static com.coditory.quark.context.ResolutionPath.emptyResolutionPath;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Collectors.toUnmodifiableSet;
public final class Context implements Closeable {
public static Context scanPackage(Class> type) {
checkNonNull(type, "type");
return builder()
.scanPackage(type)
.build();
}
public static ContextBuilder builder() {
return new ContextBuilder();
}
static Context create(Set> beanHolders, Map properties) {
Timer totalTimer = Timer.start();
Map, List>> holders = ContextResolver.resolve(beanHolders, properties);
Context context = new Context(holders, properties);
context.init();
log.info("Created context in {}", totalTimer.measureAndFormat());
return context;
}
static Context createEager(Set> beanHolders, Map properties) {
Timer totalTimer = Timer.start();
Map, List>> holders = ContextResolver.resolve(beanHolders, properties);
Context context = new Context(holders, properties);
context.init();
holders.forEach((descriptor, creator) -> {
if (descriptor.getName() != null) {
context.get(descriptor.getType(), descriptor.getName());
} else {
context.get(descriptor.getType());
}
});
log.info("Created eager context in {}", totalTimer.measureAndFormat());
return context;
}
private static final Logger log = LoggerFactory.getLogger(Context.class);
private final Map, List>> beanHoldersByType;
private final Map, List>> beanHolders;
private final Set> holders;
private final Set beanNames;
private final Map properties;
private boolean closed = false;
private Context(Map, List>> beanHolders, Map properties) {
this.beanHolders = requireNonNull(beanHolders);
this.properties = Map.copyOf(properties);
this.beanHoldersByType = groupBeanCreatorsByType(beanHolders);
this.holders = beanHolders.values().stream()
.flatMap(Collection::stream)
.collect(toUnmodifiableSet());
this.beanNames = beanHolders.keySet().stream()
.map(BeanDescriptor::getName)
.collect(toSet());
}
private void init() {
ResolutionContext context = new ResolutionContext(this, emptyResolutionPath());
this.holders.stream()
.filter(BeanHolder::isEager)
.forEach(holder -> holder.get(context));
}
private Map, List>> groupBeanCreatorsByType(Map, List>> beanCreators) {
Map, List>> beanCreatorsByType = new HashMap<>();
beanCreators.forEach((key, value) -> {
List> creators = beanCreatorsByType.computeIfAbsent(key.getType(), (k) -> new ArrayList<>());
creators.addAll(value);
});
return beanCreatorsByType.entrySet().stream()
.filter(e -> !e.getValue().isEmpty())
.collect(Collectors.toMap(Map.Entry::getKey, e -> unmodifiableList(e.getValue())));
}
public T get(Class type) {
checkNonNull(type, "type");
return get(descriptor(type), emptyResolutionPath());
}
public T getOrNull(Class type) {
checkNonNull(type, "type");
return getOrNull(descriptor(type), emptyResolutionPath());
}
public T get(Class type, String name) {
checkNonNull(type, "type");
checkNonNull(name, "name");
return get(descriptor(type, name), emptyResolutionPath());
}
public T getOrNull(Class type, String name) {
checkNonNull(type, "type");
checkNonNull(name, "name");
return getOrNull(descriptor(type, name), emptyResolutionPath());
}
T get(BeanDescriptor descriptor, ResolutionPath path) {
T bean = getOrNull(descriptor, path);
if (bean == null) {
throw new ContextException("Could not find bean: " + descriptor.toShortString());
}
return bean;
}
T getOrNull(BeanDescriptor descriptor, ResolutionPath path) {
List> holders = beanHolders.get(descriptor);
if (holders == null || holders.isEmpty()) {
return null;
}
if (holders.size() > 1) {
throw new ContextException("Expected single bean: " + descriptor.toShortString()
+ ". Got: " + holders.size());
}
BeanHolder> holder = holders.get(0);
try {
return createBean(holder, descriptor, path.add(descriptor));
} catch (Exception e) {
Throwable cause = simplifyException(e, path);
throw new ContextException("Could not create bean: " + descriptor.toShortString(), cause);
}
}
public boolean contains(Class> type) {
checkNonNull(type, "type");
return beanHoldersByType.containsKey(type);
}
public boolean contains(String name) {
checkNonNull(name, "name");
return beanNames.contains(name);
}
public boolean contains(Class> type, String name) {
checkNonNull(type, "type");
checkNonNull(name, "name");
return beanHolders.containsKey(descriptor(type, name));
}
public List getAll(Class type) {
checkNonNull(type, "type");
return getAll(type, emptyResolutionPath());
}
List getAll(Class type, ResolutionPath path) {
List beans = getAllOrEmpty(type, path);
if (beans.isEmpty()) {
throw new ContextException("Beans not found for type: " + type.getSimpleName());
}
return beans;
}
public List getAllOrEmpty(Class type) {
checkNonNull(type, "type");
return getAllOrEmpty(type, emptyResolutionPath());
}
List getAllOrEmpty(Class type, ResolutionPath path) {
BeanDescriptor descriptor = descriptor(type);
List> creators = beanHoldersByType.get(type);
if (creators == null || creators.isEmpty()) {
return List.of();
}
try {
return creators.stream()
.map(creator -> createBean(creator, descriptor, path.add(type)))
.collect(toList());
} catch (Exception e) {
Throwable cause = simplifyException(e, path);
throw new ContextException("Could not create beans: " + descriptor.toShortString(), cause);
}
}
@SuppressWarnings("unchecked")
private T createBean(BeanHolder> holder, BeanDescriptor descriptor, ResolutionPath path) {
if (closed) {
throw new ContextException("Context already closed");
}
ResolutionContext resolutionContext = new ResolutionContext(this, path);
Object bean = holder.get(resolutionContext);
return (T) bean;
}
@Override
public void close() {
ResolutionContext emptyResolutionContext = new ResolutionContext(this, emptyResolutionPath());
Set> closedBeanHolders;
long createdBeans;
do {
closedBeanHolders = holders.stream()
.filter(BeanHolder::isCached)
.collect(toSet());
closedBeanHolders.forEach(b -> b.close(emptyResolutionContext));
createdBeans = holders.stream()
.filter(BeanHolder::isCached)
.count();
} while (closedBeanHolders.size() < createdBeans);
closed = true;
}
private Throwable simplifyException(Throwable e, ResolutionPath path) {
if (!path.isEmpty()) {
return e;
}
CyclicDependencyException rootCause = Throwables.getRootCauseOfType(e, CyclicDependencyException.class);
return rootCause == null ? e : rootCause;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy