package org.mentacontainer.impl;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.mentacontainer.Component;
import org.mentacontainer.ConfigurableComponent;
import org.mentacontainer.Container;
import org.mentacontainer.Dependency;
import org.mentacontainer.Scope;
import org.mentacontainer.util.InjectionUtils;
import org.mentacontainer.util.InjectionUtils.Provider;
/**
* The implementation of the IoC container.
*
* @author sergio.oliveira.jr@gmail.com
*/
public class MentaContainer
implements Container {
private Map<String,
Component> beans =
new HashMap<String,
Component>();
private Map<String, Scope
> scopes =
new HashMap<String, Scope
>();
private Map<String,
Object> singletonsCache =
new HashMap<String,
Object>();
private Map<String,
ThreadLocal<Object>> threadLocalsCache =
new HashMap<String,
ThreadLocal<Object>>();
private Set<Dependency
> dependencies =
new HashSet<Dependency
>();
public void clear
(Scope scope
) {
if (scope == Scope.
SINGLETON) {
singletonsCache.
clear();
} else if (scope == Scope.
THREAD) {
for(ThreadLocal<Object> t : threadLocalsCache.
values()) {
t.
set(null);
}
}
}
public <T
> T get
(String key
) {
if (!beans.
containsKey(key
)) return null;
Component c = beans.
get(key
);
Scope scope = scopes.
get(key
);
Object target =
null;
try {
if (scope == Scope.
SINGLETON) {
if (singletonsCache.
containsKey(key
)) {
target = singletonsCache.
get(key
);
return (T
) target
; // no need to wire again...
} else {
target = c.
getInstance();
singletonsCache.
put(key, target
);
}
} else if (scope == Scope.
THREAD) {
if (threadLocalsCache.
containsKey(key
)) {
ThreadLocal<Object> t = threadLocalsCache.
get(key
);
target = t.
get();
if (target ==
null) { // different thread...
target = c.
getInstance();
t.
set(target
);
// don't return... let it be wired...
} else {
return (T
) target
; // no need to wire again...
}
} else {
ThreadLocal<Object> t =
new ThreadLocal<Object>();
target = c.
getInstance();
t.
set(target
);
threadLocalsCache.
put(key, t
);
// let it be wired...
}
} else if (scope == Scope.
NONE) {
target = c.
getInstance();
} else {
throw new UnsupportedOperationException("Don't know how to handle scope: " + scope
);
}
if (target
!=
null) {
for (Dependency d : dependencies
) {
// has dependency ?
Method m = d.
check(target.
getClass());
if (m
!=
null) {
String sourceKey = d.
getSource();
if (sourceKey.
equals(key
)) {
// cannot depend on itself... also avoid recursive StackOverflow...
continue;
}
Object source = get
(sourceKey
);
boolean isAssignable = source
!=
null && d.
getType().
isAssignableFrom(source.
getClass());
// check if we can find the dependency and if it is
// assignable to the target dependency
if (isAssignable
) {
try {
// inject
m.
invoke(target, source
);
} catch (Exception e
) {
throw new RuntimeException("Cannot inject dependency: method = " +
(m
!=
null ? m.
getName() :
"NULL") +
" / source = "
+
(source
!=
null ? source :
"NULL") +
" / target = " + target, e
);
}
}
}
}
}
return (T
) target
; // return target nicely with all the dependencies
} catch (Exception e
) {
throw new RuntimeException(e
);
}
}
public Component ioc
(String key,
Component component, Scope scope
) {
beans.
put(key, component
);
singletonsCache.
remove(key
); // just in case we are overriding a previous singleton bean...
threadLocalsCache.
remove(key
); // just in case we are overriding a previous thread local...
scopes.
put(key, scope
);
return component
;
}
public Component ioc
(String key,
Component component
) {
return ioc
(key, component, Scope.
NONE);
}
public ConfigurableComponent ioc
(String key,
Class<? extends Object> klass
) {
ConfigurableComponent cc =
new MentaComponent
(klass
);
ioc
(key, cc
);
return cc
;
}
public ConfigurableComponent ioc
(String key,
Class<? extends Object> klass, Scope scope
) {
ConfigurableComponent cc =
new MentaComponent
(klass
);
ioc
(key, cc, scope
);
return cc
;
}
public Dependency autowire
(Dependency d
) {
dependencies.
add(d
);
return d
;
}
public Dependency autowire
(String property,
Class<? extends Object> klass
) {
return autowire
(property, klass, property
);
}
public Dependency autowire
(String property,
Class<? extends Object> klass,
String source
) {
return autowire
(new MentaDependency
(property, klass, source
));
}
public Container populate
(Object bean
) {
Provider p =
new Provider() {
public Object get
(String key
) {
return MentaContainer.
this.
get(key
);
}
public boolean contains
(String key
) {
return MentaContainer.
this.
contains(key
);
}
};
try {
InjectionUtils.
getObject(bean, p,
false,
null,
true,
false,
true);
} catch(Exception e
) {
throw new RuntimeException("Error populating bean: " + bean, e
);
}
return this;
}
public boolean contains
(String key
) {
return beans.
containsKey(key
);
}
}