/trunk/src/main/java/org/mentacontainer/Component.java |
---|
File deleted |
\ No newline at end of file |
/trunk/src/main/java/org/mentacontainer/Dependency.java |
---|
File deleted |
\ No newline at end of file |
/trunk/src/main/java/org/mentacontainer/impl/MentaDependency.java |
---|
File deleted |
/trunk/src/main/java/org/mentacontainer/impl/MentaComponent.java |
---|
File deleted |
\ No newline at end of file |
/trunk/src/main/java/org/mentacontainer/impl/Component.java |
---|
New file |
0,0 → 1,193 |
package org.mentacontainer.impl; |
import java.lang.reflect.Constructor; |
import java.lang.reflect.Method; |
import java.util.HashMap; |
import java.util.Iterator; |
import java.util.LinkedList; |
import java.util.List; |
import java.util.Map; |
import org.mentacontainer.util.FindMethod; |
class Component { |
private Class<? extends Object> klass; |
private Map<String, Object> props = null; |
private List<Object> initValues = null; |
private Constructor<? extends Object> constructor = null; |
private Map<String, Method> cache = null; |
private final boolean singleton; |
public Component(Class<? extends Object> klass, boolean singleton) { |
this.klass = klass; |
this.singleton = singleton; |
} |
public boolean isSingleton() { return singleton; } |
public void addProperty(String name, Object value) { |
if (props == null) { |
props = new HashMap<String, Object>(); |
cache = new HashMap<String, Method>(); |
} |
props.put(name, value); |
} |
public void addInitValue(Object value) { |
if (initValues == null) { |
initValues = new LinkedList<Object>(); |
} |
initValues.add(value); |
} |
private Class<? extends Object>[] getClasses(List<Object> values) { |
Class<? extends Object>[] types = (Class<? extends Object>[]) new Class[values.size()]; |
Iterator<Object> iter = values.iterator(); |
int index = 0; |
while(iter.hasNext()) { |
Object value = iter.next(); |
if (value != null) { |
types[index++] = value.getClass(); |
} else { |
types[index++] = null; |
} |
} |
return types; |
} |
private Object [] getValues(List<Object> values) throws InstantiationException { |
Object [] array = new Object[values.size()]; |
int index = 0; |
Iterator<Object> iter = values.iterator(); |
while(iter.hasNext()) { |
Object obj = iter.next(); |
array[index++] = obj; |
} |
return array; |
} |
/* |
* Use reflection to set a property in the bean |
*/ |
private void setValue(Object bean, String name, Object value) throws InstantiationException { |
try { |
StringBuffer sb = new StringBuffer(30); |
sb.append("set"); |
sb.append(name.substring(0,1).toUpperCase()); |
if (name.length() > 1) sb.append(name.substring(1)); |
String methodName = sb.toString(); |
if (!cache.containsKey(name)) { |
Method m = null; |
try { |
m = FindMethod.getMethod(klass, methodName, new Class[] { value.getClass() }); |
} catch(Exception e) { |
// try primitive... |
Class<? extends Object> primitive = getPrimitiveFrom(value); |
if (primitive != null) { |
try { |
m = klass.getMethod(methodName, new Class[] { primitive }); |
} catch(Exception ex) { |
// not found! |
} |
} |
if (m == null) { |
throw new InstantiationException("Cannot find method or field for property: " + name); |
} |
} |
if (m != null) { |
cache.put(name, m); |
m.setAccessible(true); |
} |
} |
Method m = cache.get(name); |
if (m != null) { |
m.invoke(bean, new Object[] { value }); |
} |
} catch(Exception e) { |
throw new InstantiationException("Error trying to set a property with reflection: " + name); |
} |
} |
private static Class<? extends Object> getPrimitiveFrom(Object w) { |
if (w instanceof Boolean) { return Boolean.TYPE; } |
else if (w instanceof Byte) { return Byte.TYPE; } |
else if (w instanceof Short) { return Short.TYPE; } |
else if (w instanceof Character) { return Character.TYPE; } |
else if (w instanceof Integer) { return Integer.TYPE; } |
else if (w instanceof Long) { return Long.TYPE; } |
else if (w instanceof Float) { return Float.TYPE; } |
else if (w instanceof Double) { return Double.TYPE; } |
return null; |
} |
public Object getInstance() throws InstantiationException { |
Object obj = null; |
if (initValues != null && initValues.size() > 0) { |
if (constructor == null) { |
try { |
constructor = klass.getConstructor(getClasses(initValues)); |
} catch(Exception e) { |
throw new InstantiationException("Cannot find a constructor for class: " + klass); |
} |
} |
try { |
obj = constructor.newInstance(getValues(initValues)); |
} catch(Exception e) { |
throw new InstantiationException("Cannot create instance from constructor: " + constructor); |
} |
} else { |
try { |
obj = klass.newInstance(); |
} catch(Exception e) { |
throw new InstantiationException("Cannot create instance from class: " + klass); |
} |
} |
if (props != null && props.size() > 0) { |
Iterator<String> iter = props.keySet().iterator(); |
while(iter.hasNext()) { |
String name = iter.next(); |
Object value = props.get(name); |
setValue(obj, name, value); |
} |
} |
return obj; |
} |
} |
/trunk/src/main/java/org/mentacontainer/impl/MentaContainer.java |
---|
6,156 → 6,166 |
import java.util.Map; |
import java.util.Set; |
import org.mentacontainer.Component; |
import org.mentacontainer.Container; |
import org.mentacontainer.Dependency; |
import org.mentacontainer.util.InjectionUtils; |
/** |
* 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, Component> beans = new HashMap<String, Component>(); |
private Map<String, Object> singletons = new HashMap<String, Object>(); |
private Map<String, Object> singletons = new HashMap<String, Object>(); |
private Set<Dependency> dependencies = new HashSet<Dependency>(); |
private Set<Dependency> dependencies = new HashSet<Dependency>(); |
public Object get(String key) { |
public Object get(String key) { |
if (!beans.containsKey(key)) throw new RuntimeException("Container does not have this bean set: " + key); |
if (!beans.containsKey(key)) throw new RuntimeException("Container does not have this bean set: " + key); |
Component c = beans.get(key); |
Component c = beans.get(key); |
Object target = null; |
Object target = null; |
try { |
try { |
if (c.isSingleton()) { |
if (c.isSingleton()) { |
if (singletons.containsKey(key)) { |
if (singletons.containsKey(key)) { |
target = singletons.get(key); |
target = singletons.get(key); |
return target; |
return target; |
} else { |
} else { |
target = c.getInstance(); |
target = c.getInstance(); |
singletons.put(key, target); |
} |
singletons.put(key, target); |
} |
} else { |
} else { |
target = c.getInstance(); |
target = c.getInstance(); |
} |
} |
if (target != null) { |
if (target != null) { |
for (Dependency d : dependencies) { |
for (Dependency d : dependencies) { |
// has dependency ? |
Method m = d.check(target.getClass()); |
// has dependency ? |
Method m = d.getMethod(target.getClass()); |
if (m != null) { |
if (m != null) { |
String sourceKey = d.getSource(); |
String sourceKey = d.getSource(); |
if (sourceKey.equals(key)) { |
if (sourceKey.equals(key)) { |
// cannot depend on itself... also avoid recursive StackOverflow... |
// cannot depend on itself... |
// also avoid recursive StackOverflow... |
continue; |
continue; |
} |
} |
Object source = get(sourceKey); |
Object source = get(sourceKey); |
boolean isAssignable = source != null && d.getType().isAssignableFrom(source.getClass()); |
boolean isAssignable = source != null && d.getDependencyClass().isAssignableFrom(source.getClass()); |
// check if we can find the dependency and if it is |
// assignable to the target dependency |
if (isAssignable) { |
// check if we can find the dependency and if it is |
// assignable to the target dependency |
if (isAssignable) { |
try { |
try { |
// inject |
m.invoke(target, source); |
// inject |
m.invoke(target, source); |
} catch (Exception e) { |
} catch (Exception e) { |
throw new RuntimeException("Cannot inject dependency: method = " + (m != null ? m.getName() : "NULL") + " / source = " |
+ (source != null ? source : "NULL") + " / target = " + target, e); |
throw new RuntimeException("Cannot inject dependency: method = " + (m != null ? m.getName() : "NULL") + " / source = " |
+ (source != null ? source : "NULL") + " / target = " + target, e); |
} |
} |
} |
} |
} |
} |
} |
} |
} |
return target; // return target nicely with all the dependencies |
return target; // return target nicely with all the dependencies |
// injected |
} catch (Exception e) { |
} catch (Exception e) { |
throw new RuntimeException(e); |
} |
throw new RuntimeException(e); |
} |
} |
public Component ioc(Component component) { |
String key = component.getName(); |
public Container ioc(String key, Class<? extends Object> klass, boolean singleton) { |
if (beans.containsKey(key)) throw new RuntimeException("Container already set for the bean key: " + key); |
if (beans.containsKey(key)) throw new RuntimeException("Container already set for the bean key: " + key); |
beans.put(key, component); |
Component c = new Component(klass, singleton); |
return component; |
} |
public Component ioc(String key, Class<? extends Object> klass) { |
return ioc(new MentaComponent(key, klass)); |
} |
public Component ioc(String key, Class<? extends Object> klass, boolean singleton) { |
return ioc(new MentaComponent(key, klass, singleton)); |
} |
beans.put(key, c); |
public Dependency autowire(Dependency d) { |
return this; |
} |
if (dependencies.contains(d)) throw new RuntimeException("Dependency is already set: " + d.getTarget()); |
public Container ioc(String key, Class<? extends Object> klass) { |
dependencies.add(d); |
return ioc(key, klass, false); |
} |
return d; |
} |
public Container autowire(String property, Class<? extends Object> klass, String source) { |
public Dependency autowire(String property, Class<? extends Object> klass) { |
return autowire(property, klass, property); |
} |
Dependency d = new Dependency(property, klass, source); |
if (dependencies.contains(d)) throw new RuntimeException("Dependency is already set: " + property); |
public Dependency autowire(String property, Class<? extends Object> klass, String source) { |
return autowire(new MentaDependency(property, klass, source)); |
} |
if (!klass.isInterface()) throw new RuntimeException("Dependency must be an interface!"); |
public Container populate(Object bean) throws Exception { |
dependencies.add(d); |
InjectionUtils.getObject(bean, this, false, null, true, false, true); |
return this; |
} |
return this; |
} |
public Container autowire(String property, Class<? extends Object> klass) { |
return autowire(property, klass, property); |
} |
public boolean contains(String key) { |
public Container init(String key, Object value) { |
return beans.containsKey(key); |
} |
if (!beans.containsKey(key)) throw new RuntimeException("Container does not have this bean set: " + key); |
Component c = beans.get(key); |
c.addInitValue(value); |
return this; |
} |
public Container set(String key, String name, Object value) { |
if (!beans.containsKey(key)) throw new RuntimeException("Container does not have this bean set: " + key); |
Component c = beans.get(key); |
c.addProperty(name, value); |
return this; |
} |
public Container populate(Object bean) throws Exception { |
InjectionUtils.getObject(bean, this, false, null, true, false, true); |
return this; |
} |
public boolean contains(String key) { |
return beans.containsKey(key); |
} |
} |
/trunk/src/main/java/org/mentacontainer/impl/Dependency.java |
---|
New file |
0,0 → 1,105 |
package org.mentacontainer.impl; |
import java.lang.reflect.Method; |
import java.util.HashMap; |
import java.util.Map; |
import org.mentacontainer.util.InjectionUtils; |
public class Dependency { |
private final String target; |
private String source; |
private final Class<? extends Object> klass; |
private Map<String, Method> cache = new HashMap<String, Method>(); |
public Dependency(String target, Class<? extends Object> klass, String source) { |
this.klass = klass; |
this.target = target; |
this.source = source; |
} |
public Dependency(String target, Class<? extends Object> klass) { |
this(target, klass, target); |
} |
public String getTarget() { |
return target; |
} |
public String getSource() { |
return source; |
} |
public Class<? extends Object> getDependencyClass() { |
return klass; |
} |
public int hashCode() { |
return klass.hashCode() * 62 + target.hashCode() * 31 + source.hashCode(); |
} |
public boolean equals(Object obj) { |
if (!(obj instanceof Dependency)) return false; |
Dependency d = (Dependency) obj; |
if (!d.klass.equals(this.klass)) return false; |
return true; |
} |
public Method getMethod(Class<? extends Object> targetClass) { |
String className = targetClass.getName(); |
// first check cache... |
Method m = null; |
synchronized(cache) { |
m = cache.get(className); |
} |
if (m == null && cache.containsKey(className)) return null; // it is null... |
if (m != null) return m; |
m = InjectionUtils.findMethodToInject(targetClass, target, klass); |
if (m != null) { |
synchronized(cache) { |
cache.put(className, m); |
} |
return m; |
} |
synchronized(cache) { |
// save null to indicate there is no method here... (so you don't do again to find null !!! |
cache.put(className, null); |
} |
return null; |
} |
} |
/trunk/src/main/java/org/mentacontainer/Container.java |
---|
8,7 → 8,7 |
* <li> Setter injection for bean setup</li> |
* <li> Auto-wiring based on name and type</li> |
* <li> Singleton</li> |
* <li> Wiring of external beans with the beans configured in this container</li> |
* <li> Injection</li> |
* </ul> |
* |
* It does not get much simpler than that. |
34,9 → 34,9 |
* @param key The key representing the bean to return. The name of the bean in the container. |
* @param klass The class used to instantiate the bean, in other words, its implementation. |
* @param singleton A boolean to indicate if this bean will be a singleton. |
* @return The component created. (Fluent API) |
* @return The container itself. (Fluent API) |
*/ |
public Component ioc(String key, Class<? extends Object> klass, boolean singleton); |
public Container ioc(String key, Class<? extends Object> klass, boolean singleton); |
/** |
* Same as {@link #ioc(String, Class<? extends Object>, singleton)} except that it assumes |
44,22 → 44,14 |
* |
* @param key |
* @param klass |
* @return The component created. (Fluent API) |
* @return The container itself. (Fluent API) |
*/ |
public Component ioc(String key, Class<?extends Object> klass); |
public Container ioc(String key, Class<?extends Object> klass); |
/** |
* Set up IoC based on the component passed. |
* Configure a bean dependency to be auto-wired by the container. The dependency should be |
* an interface otherwise an exception is thrown. It works like that: |
* |
* @param component The component for the IoC. |
* @return The component passed as a parameter. (Fluent API) |
*/ |
public Component ioc(Component component); |
/** |
* Configure a bean dependency to be auto-wired by the container. In general you want the |
* type of the dependency to be an interface, for loosely couple dependencies. It works like that:<br/><br/> |
* |
* Whenever the container returns a bean, it checks to see if it has a property named <i>property</i> |
* and if the type of the property is <i>klass</i>. If it does, then it looks for a bean named |
* <i>source</i> and injects it inside the first bean it is returning. This approach is recursive |
67,28 → 59,45 |
* |
* @param property a bean property that will require another bean, in other words, the required |
* bean will be injected in the property of the bean been requested from the container. (auto-wiring by name) |
* @param klass the type of the dependency, in other words, the type of the auto-wiring. (auto-wiring by type) |
* @param klass an interface, not an implementation, defining the type of the auto-wiring. (auto-wiring by type) |
* @param source The dependency itself, coming from the container as well, in other words, the bean that will be injected in the original bean |
* @return The container itself. (Fluent API) |
*/ |
public Dependency autowire(String property, Class<? extends Object> klass, String source); |
public Container autowire(String property, Class<? extends Object> klass, String source); |
/** |
* Same as {@link #autowire(String, Class<? extends Object>)} except that it assumes that the property name will be the source name, in other words, |
* Same as {@link #get(String)} except that it passes the property name as the source name, in other words, |
* the property name is the same as the bean name that will be injected as the dependency. |
* |
* @param property |
* @param klass |
* @return The container itself. (Fluent API) |
*/ |
public Dependency autowire(String property, Class<? extends Object> klass); |
public Container autowire(String property, Class<? extends Object> klass); |
/** |
* Add a constructor parameter that will be used by the container to instantiate the bean. The container keeps the order of the parameters |
* in case this method gets called more than once for the same bean, in other words, it assumes that the bean constructor takes more than |
* one parameter. |
* |
* @param key The key representing the bean to return. The name of the bean in the container. |
* @param obj The constructor parameter value. |
* @return The container itself. (Fluent API) |
*/ |
public Dependency autowire(Dependency dependency); |
public Container init(String key, Object obj); |
/** |
* Add a property that will be injected by the container in the bean it instantiates. To be not confused with auto-wiring. Here we are just passing |
* static values to be injected in the bean. Auto-wiring wires anything because it is based on interfaces and NOT on implementations. |
* |
* @param key The key representing the bean to return. The name of the bean in the container. |
* @param name The name of the bean property (bean must provide a property setter) |
* @param obj The value of the property that will be injected. |
* @return The container itself. (Fluent API) |
*/ |
public Container set(String key, String name, Object obj); |
/** |
* Take a given bean and populate its properties with other beans coming from this container. Perhaps you can call this auto-wiring. |
* You basically checking properties of the given bean and looking for values (by name and type!) inside the container. And injecting |
* in the given bean, in other words, populating it. |