/*
* Lokomo OneCMDB - An Open Source Software for Configuration
* Management of Datacenter Resources
*
* Copyright (C) 2006 Lokomo Systems AB
*
* This program 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.
*
* This program 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. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via
* paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33
* Danderyd, Sweden.
*
*/
package org.onecmdb.core.utils.bean;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.onecmdb.core.IType;
import org.onecmdb.core.IValue;
import org.onecmdb.core.internal.model.primitivetypes.SimpleTypeFactory;
import org.onecmdb.core.utils.IBeanProvider;
/**
* Class that can populate java object's from a CI with attributes.
* The injection will only warn if attribute can not be injected.
*/
public class BeanClassInjector {
private static final String CLASS_ATTRIBUTE_NAME = "javaClass";
private Log log = LogFactory.getLog(this.getClass());
private HashMap<String, String> aliasToClassNameMap = new HashMap<String, String>();
private HashMap<String, Class> aliasToClassMap = new HashMap<String, Class>();
private List<URL> urls;
private IBeanProvider provider;
private HashMap<String, String> valueMap;
/**
* Optional only needed if the class is not included in<br>
* the startup classpath<br>
* <br>
* Location of classpath where java classes can be loaded from.
* <br>
* @param urls
*/
public void setClasspath(List<URL> urls) {
this.urls = urls;
}
public void setValueMap(HashMap<String, String> map) {
this.valueMap = map;
}
private ClassLoader getClassLoader() {
if (this.urls != null) {
return(new URLClassLoader((URL[])this.urls.toArray(), this.getClass().getClassLoader()));
}
return(this.getClass().getClassLoader());
}
public void setBeanProvider(IBeanProvider provider) {
this.provider = provider;
}
/**
* Map CI' alias names to classes that will be instantiated.
*
* @param alias
* @param javaClass
*/
public void addAliasToClass(String alias, String javaClass) {
this.aliasToClassNameMap.put(alias, javaClass);
}
public void setAliasToClass(Map<String, String> map) {
this.aliasToClassNameMap.putAll(map);
}
public Object beanToObject(CiBean ci) {
if (ci == null) {
return(null);
}
Class cl = getClassForCi(ci);
if (cl == null) {
log.warn("No class with alias " + ci.getAlias() + " found!");
return(null);
/*
throw new IllegalArgumentException("No class found for alias <"
+ alias + ">");
*/
}
Object instance = null;
// Default constructor.
if (instance == null) {
try {
instance = cl.newInstance();
} catch (Throwable e) {
log.error("Instantiation exception with alias '" + ci.getAlias() +"' class '" + cl.getName() +"' : " + e.toString());
return(null);
}
}
// Start to inject attributes
injectAttributes(instance, ci);
return (instance);
}
public void injectAttributes(Object instance, CiBean ci) {
HashMap<String, Object> map = buildAttributeMap(ci);
for (String alias : map.keySet()) {
Object o = map.get(alias);
if (valueMap != null) {
if (o instanceof String) {
String replace = valueMap.get(o);
if (replace != null) {
o = replace;
}
}
}
Character c = alias.charAt(0);
String setMethod = "set" + Character.toUpperCase(c) + alias.substring(1);
Method m = null;
if (true) {
Method methods[] = instance.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(setMethod)) {
m = method;
break;
}
}
if (m == null) {
if (!alias.equals(CLASS_ATTRIBUTE_NAME)) {
log.warn("No method '" + setMethod +"(" + o.getClass() +")' found in class '" + instance.getClass().getName() +"'");
}
continue;
}
} else {
try {
m = instance.getClass().getMethod(setMethod, new Class[] {o.getClass()});
log.debug("INJECT : " + ci.getAlias() +"." + setMethod + "(" + o + ":" + o.getClass().getSimpleName() + ")");
} catch (Throwable e) {
log.warn("Problem getting method '" + setMethod +"(" + o.getClass() +")' in class '" + instance.getClass().getName() +"' : " + e.toString());
// For now we skip the set method...
continue;
/*
throw new IllegalArgumentException(
"No method in " + cl.getName() + "." + setMethodName + "(" + o
+ ")");
*/
}
}
try {
m.invoke(instance, new Object[] {o});
} catch (Exception e) {
log.error("Can't set object '" + o.getClass() + "' on method " + setMethod +" in class '" + instance.getClass().getName() + "'");
/*
throw new IllegalArgumentException(
"Error:" + instance.getName() + "." + m.getName() + "(" + o
+ ") : " + e.toString(), e);
*/
}
}
}
private HashMap<String, Object> buildAttributeMap(CiBean ci) {
HashMap<String, Object> setterMap = new HashMap<String, Object>();
CiBean template = null;
if (ci.isTemplate()) {
template = ci;
} else {
template = this.provider.getBean(ci.getDerivedFrom());
}
HashMap<String, Object> attributeMap = new HashMap<String, Object>();
for (AttributeBean a : template.getAttributes()) {
List<ValueBean> values = ci.fetchAttributeValueBeans(a.getAlias());
if (a.getMaxOccurs().equals("0") || values == null || values.size() < 1) {
continue;
}
if (a.getMaxOccurs().equals("1")) {
// Add One object
attributeMap.put(a.getAlias(), values.get(0));
} else {
List<ValueBean> list = (List<ValueBean>) attributeMap.get(a
.getAlias());
if (list == null) {
list = new ArrayList<ValueBean>();
attributeMap.put(a.getAlias(), list);
}
for (ValueBean vBean : values) {
list.add(vBean);
}
}
}
for (String alias : attributeMap.keySet()) {
Object o = attributeMap.get(alias);
if (o instanceof ValueBean) {
Object value = getValueFromAttribute(template, (ValueBean) o);
if (value != null) {
setterMap.put(alias, value);
}
}
if (o instanceof List) {
List objects = null;
List<ValueBean> list = (List<ValueBean>) o;
for (ValueBean v : list) {
Object value = getValueFromAttribute(template, v);
if (value != null) {
if (objects == null) {
objects = new ArrayList();
}
objects.add(value);
}
}
if (objects != null) {
setterMap.put(alias, objects);
}
}
}
return (setterMap);
}
private Object getValueFromAttribute(CiBean owner, ValueBean value) {
if (value == null) {
return (null);
}
if (value.isComplexValue()) {
// Complex value.
// Check if we have a alias to build a class, else the ci is returned.
if (value.getValue() == null || value.getValue().length() == 0) {
return(null);
}
Object o = beanToObject(provider.getBean(value.getValue()));
if (o != null) {
return(o);
}
return(value);
}
// Simple type, here we return a String but we need to convert it...
Object javaType = getJavaValue(owner.getAttribute(value.getAlias()).getType(), value.getValue());
return (javaType);
}
private Object getJavaValue(String typeAlias, String valueString) {
IType type = SimpleTypeFactory.getInstance().toType(typeAlias);
if (type == null) {
throw new IllegalArgumentException("Simple Type <" + typeAlias + "> is not found!");
}
IValue value = type.parseString(valueString);
return(value.getAsJavaObject());
}
public Class getClassForCi(CiBean ci) {
if (ci == null) {
return(null);
}
String alias = ci.getAlias();
// Need a class name
String className = null;
// First check if the ci is configured with a javaClass
// attribute, use that.
List<ValueBean> clazzes = ci.fetchAttributeValueBeans(CLASS_ATTRIBUTE_NAME);
if (clazzes != null && clazzes.size() == 1) {
className = clazzes.get(0).getValue();
}
// If no className is provided by the ci,
// check the aliasToClassName map.
if (className == null) {
className = aliasToClassNameMap.get(alias);
}
Class clazz = aliasToClassMap.get(alias);
if (clazz == null) {
ClassLoader loader = getClassLoader();
if (className == null) {
clazz = getClassForCi(provider.getBean(ci.getDerivedFrom()));
} else {
try {
clazz = loader.loadClass(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class " + className + " for alias " + alias + " not found: " + e.toString(), e);
}
}
if (clazz != null) {
log.debug("Map alias '" + alias + "' to class " + clazz.getSimpleName() + "'");
aliasToClassMap.put(alias, clazz);
}
}
return(clazz);
}
}