package org.mentacontainer.impl;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.mentacontainer.ConfigurableFactory;
import org.mentacontainer.Container;
import org.mentacontainer.Factory;
import org.mentacontainer.Interceptable;
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, Factory
> factoriesByName =
new Hashtable<String, Factory
>();
private Map<Class<? extends Object>,
List<String>> factoryKeysByType =
new Hashtable<Class<? extends Object>,
List<String>>();
private Map<String, Scope
> scopes =
new Hashtable<String, Scope
>();
private Map<String,
Object> singletonsCache =
new Hashtable<String,
Object>();
private Map<String,
ThreadLocal<Object>> threadLocalsCache =
new Hashtable<String,
ThreadLocal<Object>>();
private Set<SetterDependency
> setterDependencies =
Collections.
synchronizedSet(new HashSet<SetterDependency
>());
private Set<ConstructorDependency
> constructorDependencies =
Collections.
synchronizedSet(new HashSet<ConstructorDependency
>());
@
Override
public Class<? extends Object> getType
(String key
) {
Factory factory = factoriesByName.
get(key
);
if (factory ==
null) return null;
return factory.
getType();
}
@
Override
public void clear
(Scope scope
) {
if (scope == Scope.
SINGLETON) {
List<ClearableHolder
> listToClear =
new LinkedList<ClearableHolder
>();
synchronized(this) {
for(String key : singletonsCache.
keySet()) {
Factory comp = factoriesByName.
get(key
);
if (comp
instanceof Interceptable
) {
Interceptable
<Object> c =
(Interceptable
<Object>) comp
;
Object value = singletonsCache.
get(key
);
listToClear.
add(new ClearableHolder
(c, value
));
}
}
singletonsCache.
clear();
}
// clear everything inside a non-synchronized block...
for(ClearableHolder cp : listToClear
) cp.
clear();
} else if (scope == Scope.
THREAD) {
List<ClearableHolder
> listToClear =
new LinkedList<ClearableHolder
>();
synchronized(this) {
for(String key : threadLocalsCache.
keySet()) {
Factory comp = factoriesByName.
get(key
);
if (comp
instanceof Interceptable
) {
Interceptable
<Object> c =
(Interceptable
<Object>) comp
;
ThreadLocal<Object> t = threadLocalsCache.
get(key
);
Object value = t.
get();
if (value
!=
null) listToClear.
add(new ClearableHolder
(c, value
));
}
}
for(ThreadLocal<Object> t : threadLocalsCache.
values()) {
t.
set(null);
}
}
// clear everything inside a non-synchronized block...
for(ClearableHolder cp : listToClear
) cp.
clear();
}
}
@
Override
public <T
> T clear
(String key
) {
if (!factoriesByName.
containsKey(key
)) return null;
Scope scope = scopes.
get(key
);
if (scope == Scope.
SINGLETON) {
ClearableHolder cp =
null;
Object value =
null;
synchronized(this) {
value = singletonsCache.
remove(key
);
if (value
!=
null) {
Factory comp = factoriesByName.
get(key
);
if (comp
instanceof Interceptable
) {
Interceptable
<Object> c =
(Interceptable
<Object>) comp
;
cp =
new ClearableHolder
(c, value
);
}
}
}
if (cp
!=
null) cp.
c.
onCleared(cp.
value);
return (T
) value
;
} else if (scope == Scope.
THREAD) {
ClearableHolder cp =
null;
Object retVal =
null;
synchronized(this) {
ThreadLocal<Object> t = threadLocalsCache.
get(key
);
if (t
!=
null) {
Object o = t.
get();
if (o
!=
null) {
Factory comp = factoriesByName.
get(key
);
if (comp
instanceof Interceptable
) {
Interceptable
<Object> c =
(Interceptable
<Object>) comp
;
cp =
new ClearableHolder
(c, o
);
}
t.
set(null);
retVal = o
;
}
}
}
if (cp
!=
null) cp.
c.
onCleared(cp.
value);
return (T
) retVal
;
} else if (scope == Scope.
NONE) {
return null; // always...
} else {
throw new UnsupportedOperationException("Scope not supported: " + scope
);
}
}
@
Override
public <T
> T get
(String key
) {
if (!factoriesByName.
containsKey(key
)) return null;
Factory c = factoriesByName.
get(key
);
Scope scope = scopes.
get(key
);
Object target =
null;
try {
if (scope == Scope.
SINGLETON) {
boolean needsToCreate =
false;
synchronized(this) {
if (singletonsCache.
containsKey(key
)) {
target = singletonsCache.
get(key
);
return (T
) target
; // no need to wire again...
} else {
needsToCreate =
true;
}
}
if (needsToCreate
) {
// getInstance needs to be in a non-synchronized block
target = c.
getInstance();
checkInterceptable
(c, target
);
synchronized(this) {
singletonsCache.
put(key, target
);
}
}
} else if (scope == Scope.
THREAD) {
boolean needsToCreate =
false;
boolean needsToAddToCache =
false;
ThreadLocal<Object> t =
null;
synchronized(this) {
if (threadLocalsCache.
containsKey(key
)) {
t = threadLocalsCache.
get(key
);
target = t.
get();
if (target ==
null) { // different thread...
needsToCreate =
true;
// don't return... let it be wired...
} else {
return (T
) target
; // no need to wire again...
}
} else {
t =
new ThreadLocal<Object>();
needsToCreate =
true;
needsToAddToCache =
true;
// let it be wired...
}
}
if (needsToCreate
) {
// getInstance needs to be in a non-synchronized block
target = c.
getInstance();
checkInterceptable
(c, target
);
t.
set(target
);
}
if (needsToAddToCache
) {
synchronized(this) {
threadLocalsCache.
put(key, t
);
}
}
} else if (scope == Scope.
NONE) {
target = c.
getInstance();
checkInterceptable
(c, target
);
} else {
throw new UnsupportedOperationException("Don't know how to handle scope: " + scope
);
}
if (target
!=
null) {
for (SetterDependency d : setterDependencies
) {
// 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
);
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
);
}
}
private final void checkInterceptable
(Factory f,
Object value
) {
if (f
instanceof Interceptable
) {
Interceptable i =
(Interceptable
) f
;
((Interceptable
) f
).
onCreated(value
);
}
}
private void mapeTypeToKeys
(String key, Factory factory
) {
Class<? extends Object> type = factory.
getType();
synchronized(factoryKeysByType
) {
List<String> list = factoryKeysByType.
get(type
);
if (list ==
null) {
list =
new LinkedList<String>();
factoryKeysByType.
put(type, list
);
}
list.
add(key
);
}
}
@
Override
public Factory ioc
(String key, Factory factory, Scope scope
) {
factoriesByName.
put(key, factory
);
mapeTypeToKeys
(key, factory
);
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 factory
;
}
@
Override
public Factory ioc
(String key, Factory factory
) {
return ioc
(key, factory, Scope.
NONE);
}
@
Override
public ConfigurableFactory ioc
(String key,
Class<? extends Object> klass
) {
ConfigurableFactory cc =
new ClassFactory
(this, klass
);
ioc
(key, cc
);
return cc
;
}
@
Override
public ConfigurableFactory ioc
(String key,
Class<? extends Object> klass, Scope scope
) {
ConfigurableFactory cc =
new ClassFactory
(this, klass
);
ioc
(key, cc, scope
);
return cc
;
}
@
Override
public void autowire
(String sourceFromContainer
) {
// autowire by constructor and setter...
autowireBySetter
(sourceFromContainer
);
autowireByConstructor
(sourceFromContainer
);
}
@
Override
public void autowire
(String sourceFromContainer,
String beanProperty
) {
// only by setter, because constructors does not have parameter names or properties...
autowireBySetter
(beanProperty, sourceFromContainer
);
}
private void autowireBySetter
(String targetProperty,
String sourceFromContainer
) {
Class<? extends Object> sourceType = getType
(sourceFromContainer
);
SetterDependency d =
new SetterDependency
(targetProperty, sourceFromContainer, sourceType
);
setterDependencies.
add(d
);
}
private void autowireBySetter
(String targetProperty
) {
autowireBySetter
(targetProperty, targetProperty
);
}
private void autowireByConstructor
(String sourceFromContainer
) {
Class<? extends Object> sourceType = getType
(sourceFromContainer
);
ConstructorDependency d =
new ConstructorDependency
(sourceFromContainer, sourceType
);
constructorDependencies.
add(d
);
}
Set<ConstructorDependency
> getConstructorDependencies
() {
return constructorDependencies
;
}
@
Override
public Container populate
(Object bean
) {
Provider p =
new Provider() {
@
Override
public Object get
(String key
) {
return MentaContainer.
this.
get(key
);
}
@
Override
public boolean hasValue
(String key
) {
return MentaContainer.
this.
check(key
);
}
};
try {
InjectionUtils.
getObject(bean, p,
false,
null,
true,
false,
true);
} catch(Exception e
) {
throw new RuntimeException("Error populating bean: " + bean, e
);
}
return this;
}
@
Override
public synchronized boolean check
(String key
) {
if (!factoriesByName.
containsKey(key
)) return false;
Scope scope = scopes.
get(key
);
if (scope == Scope.
NONE) {
return false; // always...
} else if (scope == Scope.
SINGLETON) {
return singletonsCache.
containsKey(key
);
} else if (scope == Scope.
THREAD) {
ThreadLocal<Object> t = threadLocalsCache.
get(key
);
if (t
!=
null) return t.
get() !=
null;
return false;
} else {
throw new UnsupportedOperationException("This scope is not supported: " + scope
);
}
}
private static class ClearableHolder
{
private Interceptable
<Object> c
;
private Object value
;
public ClearableHolder
(Interceptable
<Object> c,
Object value
) {
this.
c = c
;
this.
value = value
;
}
public void clear
() {
c.
onCleared(value
);
}
}
}