org.marid.runtime.context.BeanContext Maven / Gradle / Ivy
/*-
* #%L
* marid-runtime
* %%
* Copyright (C) 2012 - 2017 MARID software development group
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
* #L%
*/
package org.marid.runtime.context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.marid.beans.BeanTypeContext;
import org.marid.beans.MaridBean;
import org.marid.beans.RuntimeBean;
import org.marid.collections.MaridIterators;
import org.marid.runtime.event.*;
import org.marid.runtime.exception.MaridBeanNotFoundException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Stream;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
/**
* Runtime creation context.
*/
public final class BeanContext extends BeanTypeContext implements MaridRuntime, AutoCloseable {
private final BeanContext parent;
private final BeanConfiguration configuration;
private final RuntimeBean bean;
private final Object instance;
private final ConcurrentLinkedDeque children = new ConcurrentLinkedDeque<>();
private final HashSet processing = new HashSet<>();
public BeanContext(@Nullable BeanContext parent, @NotNull BeanConfiguration configuration, @NotNull RuntimeBean bean) {
this.parent = parent;
this.configuration = configuration;
this.bean = bean;
try {
configuration.fireEvent(l -> l.bootstrap(new ContextBootstrapEvent(this)), null);
this.instance = parent == null ? null : parent.create(bean, this);
configuration.fireEvent(l -> l.onPostConstruct(new BeanPostConstructEvent(this, bean.getName(), instance)), null);
for (final RuntimeBean child : bean.getChildren()) {
if (children.stream().noneMatch(b -> b.bean.getName().equals(child.getName()))) {
children.add(new BeanContext(this, configuration, child));
}
}
configuration.fireEvent(l -> l.onStart(new ContextStartEvent(this)), null);
} catch (Throwable x) {
configuration.fireEvent(l -> l.onFail(new ContextFailEvent(this, bean.getName(), x)), x::addSuppressed);
try {
close();
} catch (Throwable t) {
x.addSuppressed(t);
}
throw x;
}
}
public BeanContext(BeanConfiguration configuration, RuntimeBean root) {
this(null, configuration, root);
}
public Collection getChildren() {
return children;
}
@Override
public String getName() {
return bean.getName();
}
@Override
public BeanContext getParent() {
return parent;
}
public Object getInstance() {
return instance;
}
@Override
public Object getBean(String name) {
return getContext(name).instance;
}
public BeanContext getContext(String name) {
if (parent == null) {
throw new MaridBeanNotFoundException(name);
} else {
for (final RuntimeBean brother : parent.bean.getChildren()) {
if (brother.getName().equals(name)) {
try {
return parent.children.stream()
.filter(c -> c.bean == brother)
.findFirst()
.orElseGet(() -> {
final BeanContext c = new BeanContext(parent, configuration, brother);
parent.children.add(c);
return c;
});
} catch (CircularBeanException x) {
// continue
}
}
}
return parent.getContext(name);
}
}
@Override
@NotNull
public ClassLoader getClassLoader() {
return configuration.getPlaceholderResolver().getClassLoader();
}
@Override
public String resolvePlaceholders(String value) {
return configuration.getPlaceholderResolver().resolvePlaceholders(value);
}
@Override
public Properties getApplicationProperties() {
return configuration.getPlaceholderResolver().getProperties();
}
@NotNull
@Override
public RuntimeBean getBean() {
return bean;
}
@NotNull
@Override
public Type getBeanType(@NotNull String name) {
final BeanContext context = getContext(name);
return context.getBean().getFactory().getType(null, context);
}
private Object create(RuntimeBean bean, BeanContext context) {
if (processing.add(bean)) {
try {
return bean.getFactory().evaluate(null, null, context);
} finally {
processing.remove(bean);
}
} else {
throw new CircularBeanException();
}
}
@Override
public void close() {
final IllegalStateException e = new IllegalStateException("Runtime close exception");
try {
for (final BeanContext child : MaridIterators.iterable(children::descendingIterator)) {
try {
child.close();
} catch (Throwable x) {
e.addSuppressed(x);
}
}
final BeanPreDestroyEvent event = new BeanPreDestroyEvent(this, bean.getName(), instance, e::addSuppressed);
configuration.fireEvent(l -> l.onPreDestroy(event), e::addSuppressed);
configuration.fireEvent(l -> l.onStop(new ContextStopEvent(this)), e::addSuppressed);
} catch (Throwable x) {
e.addSuppressed(x);
} finally {
if (parent != null) {
parent.children.remove(this);
}
}
if (e.getSuppressed().length > 0) {
throw e;
}
}
public Stream contexts() {
return parent == null ? of(this) : concat(parent.contexts(), of(this));
}
public Stream children() {
return children.stream().flatMap(e -> concat(of(e), e.children()));
}
public Object findBean(@NotNull String name) {
return children()
.filter(e -> e.bean.getName().equals(name))
.findFirst()
.map(BeanContext::getInstance)
.orElseThrow(() -> new MaridBeanNotFoundException(name));
}
@Override
public String toString() {
return contexts().map(c -> c.bean.getName()).collect(joining("/"));
}
private static final class CircularBeanException extends RuntimeException {
private CircularBeanException() {
super(null, null, false, false);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy