
com.caucho.v5.inject.impl.InjectorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of baratine Show documentation
Show all versions of baratine Show documentation
A reactive Java web server.
/*
* Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
*
* This file is part of Baratine(TM)(TM)
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Baratine is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Baratine 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Baratine; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.v5.inject.impl;
import java.lang.annotation.Annotation;
import java.lang.ref.SoftReference;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Scope;
import com.caucho.v5.inject.BindingAmp;
import com.caucho.v5.inject.BindingInject;
import com.caucho.v5.inject.InjectProgram;
import com.caucho.v5.inject.InjectProvider;
import com.caucho.v5.inject.InjectorAmp;
import com.caucho.v5.inject.type.TypeRef;
import com.caucho.v5.loader.DynamicClassLoader;
import com.caucho.v5.loader.EnvLoader;
import com.caucho.v5.loader.EnvironmentLocal;
import com.caucho.v5.util.L10N;
import io.baratine.config.Config;
import io.baratine.convert.Convert;
import io.baratine.convert.ConvertManager;
import io.baratine.inject.Binding;
import io.baratine.inject.InjectionPoint;
import io.baratine.inject.Key;
/**
* The injection manager for a given environment.
*/
public class InjectorImpl implements InjectorAmp
{
private static final L10N L = new L10N(InjectorImpl.class);
private static final Logger log = Logger.getLogger(InjectorImpl.class.getName());
private static final EnvironmentLocal _localManager
= new EnvironmentLocal<>();
private static final WeakHashMap> _loaderManagerMap
= new WeakHashMap<>();
private final HashMap,Supplier>> _scopeMap;
private final Config _config;
private HashSet> _qualifierSet = new HashSet<>();
private ConcurrentHashMap,BindingSet>> _bindingSetMap
= new ConcurrentHashMap<>();
private ClassLoader _loader;
private ArrayList _providerList = new ArrayList<>();
private ConcurrentHashMap,Provider>> _providerMap
= new ConcurrentHashMap,Provider>>();
private ConcurrentHashMap,Provider>> _providerDefaultMap
= new ConcurrentHashMap,Provider>>();
private Provider _convertManager;
private InjectAutoBind[] _autoBind;
InjectorImpl(InjectorBuilderImpl builder)
{
_loader = builder.getClassLoader();
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_loader);
_config = builder.config();
_scopeMap = builder.scopeMap();
//_qualifierSet.add(Lookup.class);
ArrayList autoBindList = new ArrayList<>();
for (InjectAutoBind autoBind : builder.autoBind()) {
autoBindList.add(autoBind);
}
InjectAutoBind []autoBind = new InjectAutoBind[autoBindList.size()];
autoBindList.toArray(autoBind);
_autoBind = autoBind;
/*
for (Class> beanType : builder.beans()) {
addBean(beanType);
}
*/
builder.bind(this);
if (builder.isContext()) {
current(_loader, this);
}
bind();
} finally {
thread.setContextClassLoader(oldLoader);
}
}
/**
* Returns the current inject manager.
*/
public static InjectorImpl current(ClassLoader loader)
{
if (loader instanceof DynamicClassLoader) {
return _localManager.getLevel(loader);
}
else {
SoftReference injectRef = _loaderManagerMap.get(loader);
if (injectRef != null) {
return injectRef.get();
}
else {
return null;
}
}
}
/**
* Creates a new inject manager.
*/
public static InjectorImpl create()
{
return create(Thread.currentThread().getContextClassLoader());
}
/**
* Creates a new inject manager.
*/
public static InjectorImpl create(ClassLoader loader)
{
synchronized (loader) {
if (loader instanceof DynamicClassLoader) {
InjectorImpl inject = _localManager.getLevel(loader);
if (inject == null) {
inject = (InjectorImpl) InjectorAmp.manager(loader).get();
_localManager.set(inject, loader);
}
return inject;
}
else {
SoftReference injectRef = _loaderManagerMap.get(loader);
InjectorImpl inject = null;
if (injectRef != null) {
inject = injectRef.get();
if (inject != null) {
return inject;
}
}
inject = (InjectorImpl) InjectorAmp.manager(loader).get();
_loaderManagerMap.put(loader, new SoftReference<>(inject));
return inject;
}
}
}
/**
* Creates a new inject manager.
*/
private static void current(ClassLoader loader, InjectorImpl manager)
{
Objects.requireNonNull(manager);
synchronized (loader) {
if (loader instanceof DynamicClassLoader) {
_localManager.set(manager, loader);
}
else {
_loaderManagerMap.put(loader, new SoftReference<>(manager));
}
}
}
/**
* Creates a new inject manager.
*/
public static InjectorBuilderImpl manager(ClassLoader loader)
{
return new InjectorBuilderImpl(loader);
}
/**
* A configuration property.
*/
@Override
public String property(String key)
{
return _config.get(key);
}
/**
* The configuration object.
*/
@Override
public Config config()
{
return _config;
}
@Override
public T instance(Class type)
{
Key key = Key.of(type);
return instance(key);
}
@Override
public T instance(Key key)
{
Objects.requireNonNull(key);
Class type = (Class) key.rawClass();
if (type.equals(Provider.class)) {
TypeRef typeRef = TypeRef.of(key.type());
TypeRef param = typeRef.param(0);
return (T) provider(Key.of(param.type()));
}
Provider provider = provider(key);
if (provider != null) {
return provider.get();
}
else {
return null;
}
}
@Override
public T instance(InjectionPoint ip)
{
Objects.requireNonNull(ip);
Provider provider = provider(ip);
if (provider != null) {
return provider.get();
}
else {
return null;
}
}
/*
@Override
public T instance(Class type, X param)
{
Key key = Key.of(type);
return instance(key, param);
}
public T instance(Key type, X param)
{
Class paramType = (Class) param.getClass();
Function fun = function(type, paramType);
if (fun != null) {
return fun.apply(param);
}
else {
return null;
}
}
*/
@Override
public Provider provider(InjectionPoint ip)
{
Objects.requireNonNull(ip);
Provider provider = lookupProvider(ip);
if (provider != null) {
return provider;
}
return autoProvider(ip);
}
@Override
public Provider provider(Key key)
{
Objects.requireNonNull(key);
Provider provider = (Provider) _providerMap.get(key);
if (provider == null) {
provider = lookupProvider(key);
if (provider == null) {
provider = autoProvider(key);
}
_providerMap.putIfAbsent(key, provider);
provider = (Provider) _providerMap.get(key);
}
return provider;
}
private Provider lookupProvider(Key key)
{
BindingInject bean = findBean(key);
if (bean != null) {
return bean.provider();
}
BindingAmp binding = findBinding(key);
if (binding != null) {
return binding.provider();
}
binding = findObjectBinding(key);
if (binding != null) {
return binding.provider(InjectionPoint.of(key));
}
return null;
}
private Provider lookupProvider(InjectionPoint ip)
{
Key key = ip.key();
BindingInject bean = findBean(key);
if (bean != null) {
return bean.provider(ip);
}
BindingAmp provider = findBinding(key);
if (provider != null) {
return provider.provider(ip);
}
provider = findObjectBinding(key);
if (provider != null) {
return provider.provider(ip);
}
return null;
}
/**
* Create a provider based on registered auto-binders.
*/
private Provider autoProvider(Key key)
{
for (InjectAutoBind autoBind : _autoBind) {
Provider provider = autoBind.provider(this, key);
if (provider != null) {
return provider;
}
}
return providerDefault(key);
}
/**
* Create a provider based on registered auto-binders.
*/
private Provider autoProvider(InjectionPoint ip)
{
for (InjectAutoBind autoBind : _autoBind) {
Provider provider = autoBind.provider(this, ip);
if (provider != null) {
return provider;
}
}
return providerDefault(ip.key());
}
private Provider providerDefault(Key key)
{
Objects.requireNonNull(key);
Provider provider = (Provider) _providerDefaultMap.get(key);
if (provider == null) {
provider = createProvider(key);
_providerDefaultMap.putIfAbsent(key, provider);
provider = (Provider) _providerDefaultMap.get(key);
}
return provider;
}
/**
* default provider
*/
private Provider createProvider(Key key)
{
Class type = (Class) key.rawClass();
if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
return ()->null;
}
int priority = 0;
// auto-provider is factory
InjectScope scope = findScope(type);
BindingAmp binding = new ProviderConstructor<>(this, key, priority, scope, type);
binding.bind();
return binding.provider();
}
private InjectScope findScope(AnnotatedElement annElement)
{
for (Annotation ann : annElement.getAnnotations()) {
Class extends Annotation> annType = ann.annotationType();
if (annType.isAnnotationPresent(Scope.class)) {
Supplier> scopeGen = (Supplier) _scopeMap.get(annType);
if (scopeGen != null) {
return scopeGen.get();
}
else {
log.fine(L.l("@{0} is an unknown scope", annType.getSimpleName()));
}
}
}
return new InjectScopeFactory<>();
}
@Override
public Iterable> bindings(Class type)
{
BindingSet set = (BindingSet) _bindingSetMap.get(type);
if (set != null) {
return (Iterable) set;
}
else {
return Collections.EMPTY_LIST;
}
}
@Override
public List> bindings(Key key)
{
BindingSet set = (BindingSet) _bindingSetMap.get(key.rawClass());
if (set != null) {
return set.bindings(key);
}
else {
return Collections.EMPTY_LIST;
}
}
InjectScope scope(Class extends Annotation> scopeType)
{
Supplier> scopeGen = (Supplier) _scopeMap.get(scopeType);
if (scopeGen == null) {
throw error("{0} is an unknown scope",
scopeType.getSimpleName());
}
return scopeGen.get();
}
@Override
public Consumer injector(Class type)
{
ArrayList injectList = new ArrayList<>();
introspectInject(injectList, type);
introspectInit(injectList, type);
return new InjectProgramImpl(injectList);
}
@Override
public Provider> []program(Parameter []params)
{
Provider> []program = new Provider>[params.length];
for (int i = 0; i < program.length; i++) {
//Key> key = Key.of(params[i]);
program[i] = provider(InjectionPoint.of(params[i]));
}
return program;
}
private BindingInject findBean(Key key)
{
for (InjectProvider provider : _providerList) {
BindingInject bean = (BindingInject) provider.lookup(key.rawClass());
if (bean != null) {
return bean;
}
}
return null;
}
/**
* Introspect for @Inject fields.
*/
//@Override
private void introspectInject(List program, Class> type)
{
if (type == null) {
return;
}
introspectInjectField(program, type);
introspectInjectMethod(program, type);
}
private void introspectInjectField(List program, Class> type)
{
if (type == null) {
return;
}
introspectInjectField(program, type.getSuperclass());
for (Field field : type.getDeclaredFields()) {
if (! field.isAnnotationPresent(Inject.class)) {
continue;
}
/*
ArrayList annList = new ArrayList<>();
for (Annotation ann : field.getAnnotations()) {
if (isQualifier(ann)) {
annList.add(ann);
}
}
Annotation []qualifiers = new Annotation[annList.size()];
annList.toArray(qualifiers);
*/
program.add(new InjectField(this, field));
}
}
public void introspectInjectMethod(List program, Class> type)
{
if (type == null) {
return;
}
introspectInjectMethod(program, type.getSuperclass());
for (Method method : type.getDeclaredMethods()) {
if (! method.isAnnotationPresent(Inject.class)) {
continue;
}
program.add(new InjectMethod(this, method));
}
}
/**
* Introspect for @PostConstruct methods.
*/
//@Override
private void introspectInit(List program, Class> type)
{
if (type == null) {
return;
}
introspectInit(program, type.getSuperclass());
try {
for (Method method : type.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
// XXX: program.add(new PostConstructProgram(Config.getCurrent(), method));
}
}
} catch (Throwable e) {
log.log(Level.FINEST, e.toString(), e);
}
}
/**
* Adds a new injection producer to the discovered producer list.
*/
void addProvider(BindingAmp binding)
{
// TypeRef typeRef = TypeRef.of(producer.key().type());
Class type = (Class) binding.key().rawClass();
addBinding(type, binding);
}
/**
* Adds a new injection producer to the discovered producer list.
*/
void addFunction(BindingAmp binding)
{
// TypeRef typeRef = TypeRef.of(producer.key().type());
Class type = (Class) binding.key().rawClass();
addBinding(type, binding);
}
/**
* Adds a new injection producer to the discovered producer list.
*/
private void addBinding(Class type, BindingAmp binding)
{
synchronized (_bindingSetMap) {
BindingSet set = (BindingSet) _bindingSetMap.get(type);
if (set == null) {
set = new BindingSet<>(type);
_bindingSetMap.put(type, set);
}
set.addBinding(binding);
}
}
/**
* Returns an object producer.
*/
private BindingAmp findObjectBinding(Key key)
{
Objects.requireNonNull(key);
if (key.qualifiers().length != 1) {
throw new IllegalArgumentException();
}
return (BindingAmp) findBinding(Key.of(Object.class,
key.qualifiers()[0]));
}
/**
* Finds a producer for the given target type.
*/
private BindingAmp findBinding(Key key)
{
BindingSet set = (BindingSet) _bindingSetMap.get(key.rawClass());
if (set != null) {
BindingAmp binding = set.find(key);
if (binding != null) {
return binding;
}
}
return null;
}
private void bind()
{
for (BindingSet> bindingSet : _bindingSetMap.values()) {
bindingSet.bind();
}
}
@Override
public Convert converter(Class source, Class target)
{
if (_convertManager == null) {
_convertManager = provider(Key.of(ConvertManager.class));
}
return _convertManager.get().converter(source, target);
}
private RuntimeException error(String msg, Object ...args)
{
return new InjectException(L.l(msg, args));
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + EnvLoader.getId(_loader) + "]";
}
private static class InjectProgramImpl implements Consumer
{
private InjectProgram []_program;
InjectProgramImpl(ArrayList programList)
{
_program = new InjectProgram[programList.size()];
programList.toArray(_program);
}
@Override
public void accept(T bean)
{
Objects.requireNonNull(bean);
InjectContext env = InjectContextImpl.CONTEXT;
for (InjectProgram program : _program) {
program.inject(bean, env);
}
}
}
static class BindingSet implements Iterable>
{
private Class _type;
private ArrayList> _list = new ArrayList<>();
BindingSet(Class type)
{
_type = type;
}
void addBinding(BindingAmp binding)
{
_list.add(binding);
_list.sort((x,y)->compareBinding(x,y));
}
private int compareBinding(BindingAmp x, BindingAmp y)
{
int cmp = y.priority() - x.priority();
return Integer.signum(cmp);
}
void bind()
{
for (BindingAmp binding : _list) {
binding.bind();
}
}
BindingAmp find(Key key)
{
for (BindingAmp binding : _list) {
if (key.isAssignableFrom(binding.key())) {
return binding;
}
}
return null;
}
public Function findFunction(Key key, Class paramType)
{
for (BindingAmp binding : _list) {
if (key.isAssignableFrom(binding.key())) {
Function fun = binding.function(paramType);
if (fun != null) {
return fun;
}
}
}
return null;
}
public List> bindings(Key key)
{
List> bindings = new ArrayList<>();
for (BindingAmp binding : _list) {
if (key.isAssignableFrom(binding.key())) {
bindings.add(binding);
}
}
return bindings;
}
@Override
public Iterator> iterator()
{
return _list.iterator();
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + _type.getName() + "]";
}
}
private static class KeyFunction
{
private Key> _key;
private Class> _paramType;
KeyFunction(Key> key, Class> paramType)
{
_key = key;
_paramType = paramType;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy