MentaContainer

Compare Revisions

Ignore whitespace Rev 20 → Rev 5

/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.