org.rapidoid.ioc.impl.IoCContextImpl Maven / Gradle / Ivy
package org.rapidoid.ioc.impl;
import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.cls.Cls;
import org.rapidoid.collection.Coll;
import org.rapidoid.commons.AnyObj;
import org.rapidoid.commons.Deep;
import org.rapidoid.config.Conf;
import org.rapidoid.config.Config;
import org.rapidoid.ioc.*;
import org.rapidoid.lambda.Lmbd;
import org.rapidoid.lambda.Mapper;
import org.rapidoid.log.Log;
import org.rapidoid.scan.Scan;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;
import org.rapidoid.util.MscOpts;
import org.rapidoid.util.Once;
import javax.annotation.PostConstruct;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/*
* #%L
* rapidoid-inject
* %%
* Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors
* %%
* 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.
* #L%
*/
@Authors("Nikolche Mihajlovski")
@Since("5.1.0")
public class IoCContextImpl extends RapidoidThing implements IoCContext {
public IoCContextImpl() {
}
private volatile String name;
private volatile IoCState state = new IoCState();
private volatile BeanProvider beanProvider;
private volatile IoCContextWrapper wrapper;
private final Map, ClassMetadata> metadata = Coll
.autoExpandingMap(new Mapper, ClassMetadata>() {
@Override
public ClassMetadata map(Class> clazz) throws Exception {
return new ClassMetadata(clazz);
}
});
private final Once activate = new Once();
@Override
public IoCContext name(String name) {
this.name = name;
return this;
}
@Override
public String name() {
return name;
}
@Override
public synchronized void reset() {
if (!state.isEmpty()) {
Log.info("Resetting IoC context", "context", this);
} else {
Log.debug("Resetting IoC context", "context", this);
}
state.reset();
metadata.clear();
beanProvider = null;
activate.reset();
}
private ClassMetadata meta(Class> type) {
return metadata.get(type);
}
@Override
public synchronized void manage(Object... classesOrInstances) {
classesOrInstances = AnyObj.flat(classesOrInstances);
List> autoCreate = U.list();
for (Object classOrInstance : classesOrInstances) {
boolean isClass = isClass(classOrInstance);
Class> clazz = Cls.toClass(classOrInstance);
if (Msc.matchingProfile(clazz)) {
for (Class> interfacee : Cls.getImplementedInterfaces(clazz)) {
addProvider(interfacee, classOrInstance);
}
if (isClass) {
Log.debug("configuring managed class", "class", classOrInstance);
state.providedClasses.add(clazz);
if (!clazz.isInterface() && !clazz.isEnum() && !clazz.isAnnotation()) {
// if the class is annotated, auto-create an instance
if (clazz.getAnnotation(Autocreate.class) != null) {
autoCreate.add(clazz);
}
}
} else {
Object instance = classOrInstance;
Log.debug("configuring provided instance", "instance", instance);
addProvider(clazz, instance);
state.providedInstances.add(instance);
state.instances.add(instance);
}
}
}
for (Class> clazz : autoCreate) {
singleton(clazz);
}
}
private void addProvider(Class> type, Object provider) {
state.providersByType.get(type).add(provider);
}
@Override
public synchronized T singleton(Class type) {
Log.debug("Singleton", "type", type);
return provideIoCInstanceOf(null, type, null, null, false);
}
@Override
public synchronized boolean autowire(Object target) {
Log.debug("Autowire", "target", target);
return autowire(target, null, null, null);
}
@Override
public synchronized T autowire(T target, Mapper session, Mapper bindings) {
Log.debug("Autowire", "target", target);
autowire(target, null, session, bindings);
return target;
}
@Override
public synchronized T inject(T target) {
Log.debug("Inject", "target", target);
return register(target, null);
}
@Override
public synchronized T inject(T target, Map properties) {
Log.debug("Inject", "target", target, "properties", properties);
return register(target, properties);
}
private T provideSessionValue(Object target, Class type, String name, Mapper session) {
U.notNull(session, "session");
Object value = Lmbd.eval(session, name);
return value != null ? Cls.convert(value, type) : null;
}
private T provideBindValue(Object target, Class type, String name, Mapper bindings) {
U.notNull(bindings, "bindings");
Object value = Lmbd.eval(bindings, name);
return value != null ? Cls.convert(value, type) : null;
}
@SuppressWarnings("unchecked")
private T provideIoCInstanceOf(Object target, Class type, String name,
Map properties, boolean optional) {
if (target != null) {
processMetadata(meta(target.getClass()));
}
if (type != null) {
processMetadata(meta(type));
}
T instance = (T) provideSpecialInstance(type, name);
if (instance == null && name != null) {
instance = provideInstanceByName(target, type, name, properties);
}
if (instance == null) {
instance = provideInstanceByType(type, properties);
}
BeanProvider provider = this.beanProvider;
if (instance == null && provider != null) {
instance = provider.getBean(type, name);
}
if (instance == null && Cls.isAppBeanType(type)) {
instance = provideNewInstanceOf(type, properties);
}
if (!optional) {
if (instance == null) {
if (name != null) {
throw U.rte("Didn't find a value for type '%s' and name '%s'!", type, name);
} else {
throw U.rte("Didn't find a value for type '%s'!", type);
}
}
}
return Cls.isAppBean(instance) ? register(instance, properties) : null;
}
private boolean processMetadata(ClassMetadata meta) {
boolean processed = false;
if (U.notEmpty(meta.typesToManage)) {
manage(meta.typesToManage);
processed = true;
}
if (U.notEmpty(meta.packagesToScan)) {
List> classes = Scan.annotated(IoC.ANNOTATIONS).in(meta.packagesToScan).loadAll();
if (U.notEmpty(classes)) {
manage(classes);
processed = true;
}
}
return processed;
}
@SuppressWarnings("unchecked")
private T provideNewInstanceOf(Class type, Map properties) {
// instantiation if it's real class
if (!type.isInterface() && !type.isEnum() && !type.isAnnotation()) {
if (Msc.matchingProfile(type)) {
T instance = instantiate(type, properties);
return register(instance, properties);
}
}
return null;
}
private T instantiate(Class type, Map properties) {
ClassMetadata meta = meta(type);
if (U.notEmpty(meta.injectableConstructors)) {
U.must(meta.injectableConstructors.size() == 1, "Found more than 1 @Inject-annotated constructor for: %s", type);
Constructor> constr = U.single(meta.injectableConstructors);
Class>[] paramTypes = constr.getParameterTypes();
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < args.length; i++) {
Class> paramType = paramTypes[i];
String paramName = null; // FIXME inject by name
args[i] = provideIoCInstanceOf(null, paramType, paramName, properties, false);
}
return Cls.invoke(constr, args);
} else if (meta.defaultConstructor != null) {
return Cls.invoke(meta.defaultConstructor);
} else {
throw U.illegal("Cannot find a default nor @Inject-annotated constructor for: %s", type);
}
}
private T provideInstanceByType(Class type, Map properties) {
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy