/*
* JBoss, Home of Professional Open Source
* Copyright 2007, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.managed.plugins.factory;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.jboss.beans.info.spi.BeanInfo;
import org.jboss.beans.info.spi.PropertyInfo;
import org.jboss.config.spi.Configuration;
import org.jboss.logging.Logger;
import org.jboss.managed.api.Fields;
import org.jboss.managed.api.ManagedObject;
import org.jboss.managed.api.ManagedOperation;
import org.jboss.managed.api.ManagedParameter;
import org.jboss.managed.api.ManagedProperty;
import org.jboss.managed.api.MutableManagedObject;
import org.jboss.managed.api.ManagedOperation.Impact;
import org.jboss.managed.api.annotation.ActivationPolicy;
import org.jboss.managed.api.annotation.AnnotationDefaults;
import org.jboss.managed.api.annotation.ConstraintsPopulatorFactory;
import org.jboss.managed.api.annotation.DefaultValueBuilderFactory;
import org.jboss.managed.api.annotation.FieldsFactory;
import org.jboss.managed.api.annotation.ManagementComponent;
import org.jboss.managed.api.annotation.ManagementConstants;
import org.jboss.managed.api.annotation.ManagementDeployment;
import org.jboss.managed.api.annotation.ManagementObject;
import org.jboss.managed.api.annotation.ManagementObjectID;
import org.jboss.managed.api.annotation.ManagementObjectRef;
import org.jboss.managed.api.annotation.ManagementOperation;
import org.jboss.managed.api.annotation.ManagementParameter;
import org.jboss.managed.api.annotation.ManagementProperties;
import org.jboss.managed.api.annotation.ManagementProperty;
import org.jboss.managed.api.annotation.ManagementPropertyFactory;
import org.jboss.managed.api.annotation.ManagementRuntimeRef;
import org.jboss.managed.api.annotation.Masked;
import org.jboss.managed.api.annotation.RunStateProperty;
import org.jboss.managed.api.annotation.ViewUse;
import org.jboss.managed.api.factory.ManagedObjectFactory;
import org.jboss.managed.api.factory.ManagedObjectDefinition;
import org.jboss.managed.plugins.DefaultFieldsImpl;
import org.jboss.managed.plugins.ManagedObjectImpl;
import org.jboss.managed.plugins.ManagedOperationImpl;
import org.jboss.managed.plugins.ManagedParameterImpl;
import org.jboss.managed.plugins.WritethroughManagedPropertyImpl;
import org.jboss.managed.spi.factory.InstanceClassFactory;
import org.jboss.managed.spi.factory.ManagedObjectBuilder;
import org.jboss.managed.spi.factory.ManagedObjectPopulator;
import org.jboss.managed.spi.factory.ManagedParameterConstraintsPopulator;
import org.jboss.managed.spi.factory.ManagedParameterConstraintsPopulatorFactory;
import org.jboss.managed.spi.factory.ManagedPropertyConstraintsPopulator;
import org.jboss.managed.spi.factory.ManagedPropertyConstraintsPopulatorFactory;
import org.jboss.metadata.spi.MetaData;
import org.jboss.metadata.spi.signature.FieldSignature;
import org.jboss.metadata.spi.signature.MethodSignature;
import org.jboss.metadata.spi.signature.Signature;
import org.jboss.metatype.api.annotations.MetaMapping;
import org.jboss.metatype.api.annotations.MetaMappingFactory;
import org.jboss.metatype.api.types.ArrayMetaType;
import org.jboss.metatype.api.types.CollectionMetaType;
import org.jboss.metatype.api.types.GenericMetaType;
import org.jboss.metatype.api.types.MetaType;
import org.jboss.metatype.api.types.MetaTypeFactory;
import org.jboss.metatype.api.values.MetaValue;
import org.jboss.metatype.api.values.MetaValueFactory;
import org.jboss.metatype.plugins.values.defaults.BigDecimalDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.BigIntegerDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.BooleanDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.BooleanPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.ByteDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.BytePrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.CharDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.CharPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.DoubleDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.DoublePrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.FloatDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.FloatPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.IntDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.IntPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.LongDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.LongPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.PropertiesDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.ShortDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.ShortPrimitiveDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.StringDefaultValueBuilder;
import org.jboss.metatype.plugins.values.defaults.StringObjectNameDefaultBuilder;
import org.jboss.metatype.spi.values.DefaultValueBuilder;
import org.jboss.metatype.spi.values.MetaMapper;
import org.jboss.metatype.spi.values.MetaMapperFactory;
import org.jboss.reflect.spi.AnnotatedInfo;
import org.jboss.reflect.spi.ClassInfo;
import org.jboss.reflect.spi.MethodInfo;
import org.jboss.reflect.spi.ParameterInfo;
import org.jboss.reflect.spi.TypeInfo;
/**
* The base ManagedObjectFactory implementation.
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @author Scott.Stark@jboss.org
* @version $Revision: 87524 $
*/
public class AbstractManagedObjectFactory extends ManagedObjectFactory
implements ManagedObjectBuilder
{
private static final Logger log = Logger.getLogger(AbstractManagedObjectFactory.class);
/** The configuration */
private static final Configuration configuration = PropertyConfigurationAccess.getConfiguration();
/** The managed object meta type */
public static final GenericMetaType MANAGED_OBJECT_META_TYPE = new GenericMetaType(ManagedObject.class.getName(), ManagedObject.class.getName());
/** The meta type factory */
private MetaTypeFactory metaTypeFactory = MetaTypeFactory.getInstance();
/** The meta value factory */
private MetaValueFactory metaValueFactory = MetaValueFactory.getInstance();
/** A default InstanceClassFactory used when there is no explicit ICF for a given class */
private InstanceClassFactory<?> defaultInstanceFactory;
/** A default ManagedObjectPopulator used when there is no explicit ManagedObjectBuilder for a given class */
private ManagedObjectPopulator<?> defaultManagedObjectPopulator;
/** The managed object builders */
private Map<Class<?>, ManagedObjectBuilder> builders = new WeakHashMap<Class<?>, ManagedObjectBuilder>();
/** The instance to class factories */
private Map<Class<?>, InstanceClassFactory<?>> instanceFactories = new WeakHashMap<Class<?>, InstanceClassFactory<?>>();
/** The default value builders */
private Map<MetaType, DefaultValueBuilder> defaultBuilders = new HashMap<MetaType, DefaultValueBuilder>();
/**
* Create a ManagedProperty by looking to the factory for ctor(Fields)
* @param factory - the ManagedProperty implementation class
* @param fields - the fields to pass to the ctor
* @return the managed property if successful, null otherwise
*/
public static ManagedProperty createManagedProperty(Class<? extends ManagedProperty> factory, Fields fields)
{
ManagedProperty property = null;
try
{
Class<?>[] sig = {Fields.class};
Constructor<? extends ManagedProperty> ctor = factory.getConstructor(sig);
Object[] args = {fields};
property = ctor.newInstance(args);
}
catch(Exception e)
{
log.debug("Failed to create ManagedProperty", e);
}
return property;
}
/**
* Create an AbstractManagedObjectFactory that uses an AbstractInstanceClassFactory
* as the defaultInstanceFactory and AbstractManagedObjectPopulator as the
* defaultManagedObjectPopulator. The MetaTypeFactory, MetaValueFactory are
* obtained from the respective getInstance() factory methods.
*/
public AbstractManagedObjectFactory()
{
// Create an AbstractInstanceClassFactory as the default ICF
DefaultInstanceClassFactory icf = new DefaultInstanceClassFactory();
icf.setMof(this);
defaultInstanceFactory = icf;
// Create an AbstractManagedObjectPopulator as the default
defaultManagedObjectPopulator = new AbstractManagedObjectPopulator<Serializable>(configuration, icf, instanceFactories);
initDefaultValueBuilders();
}
/**
* Create an AbstractManagedObjectFactory the given factories, supporting
* information.
*
* @param metaTypeFactory
* @param metaValueFactory
* @param defaultInstanceFactory
* @param defaultManagedObjectPopulator
* @param builders
* @param instanceFactories
*/
public AbstractManagedObjectFactory(MetaTypeFactory metaTypeFactory,
MetaValueFactory metaValueFactory,
InstanceClassFactory<?> defaultInstanceFactory,
ManagedObjectPopulator<?> defaultManagedObjectPopulator,
Map<Class<?>, ManagedObjectBuilder> builders,
Map<Class<?>, InstanceClassFactory<?>> instanceFactories)
{
this.metaTypeFactory = metaTypeFactory;
this.metaValueFactory = metaValueFactory;
this.defaultInstanceFactory = defaultInstanceFactory;
this.defaultManagedObjectPopulator = defaultManagedObjectPopulator;
this.builders = builders;
this.instanceFactories = instanceFactories;
initDefaultValueBuilders();
}
/**
* Add managed object definition.
*
* @param definition the MO definition
*/
public void addManagedObjectDefinition(ManagedObjectDefinition definition)
{
if (definition == null || definition.isValid() == false)
throw new IllegalArgumentException("Invalid MO definition: " + definition);
setBuilder(definition.getType(), definition.getBuilder());
}
/**
* Remove managed object definition.
*
* @param definition the MO definition
*/
public void removeManagedObjectDefinition(ManagedObjectDefinition definition)
{
if (definition == null)
return;
setBuilder(definition.getType(), null);
}
public void addDefaultValueBuilder(DefaultValueBuilder builder)
{
if(builder == null)
throw new IllegalArgumentException("Null DefaultValueBuilder");
setDefaultsBuilder(builder.getType(), builder);
}
public void setDefaultsBuilder(MetaType type, DefaultValueBuilder builder)
{
synchronized (defaultBuilders)
{
if (builder == null)
defaultBuilders.remove(type);
else
defaultBuilders.put(type, builder);
}
}
public void removeDefaltValueBuilder(DefaultValueBuilder builder)
{
if (builder == null)
return;
setDefaultsBuilder(builder.getType(), null);
}
/**
* Get the configuration
*
* @return the configuration
*/
public Configuration getConfiguration()
{
return configuration;
}
public MetaTypeFactory getMetaTypeFactory()
{
return metaTypeFactory;
}
public void setMetaTypeFactory(MetaTypeFactory metaTypeFactory)
{
this.metaTypeFactory = metaTypeFactory;
}
public MetaValueFactory getMetaValueFactory()
{
return metaValueFactory;
}
public void setMetaValueFactory(MetaValueFactory metaValueFactory)
{
this.metaValueFactory = metaValueFactory;
}
public Map<Class<?>, ManagedObjectBuilder> getBuilders()
{
return builders;
}
public void setBuilders(Map<Class<?>, ManagedObjectBuilder> builders)
{
this.builders = builders;
}
public Map<Class<?>, InstanceClassFactory<?>> getInstanceFactories()
{
return instanceFactories;
}
public void setInstanceFactories(
Map<Class<?>, InstanceClassFactory<?>> instanceFactories)
{
this.instanceFactories = instanceFactories;
}
/**
* Get the default InstanceClassFactory
* @return the current default InstanceClassFactory
*/
public InstanceClassFactory<?> getDefaultInstanceFactory()
{
return defaultInstanceFactory;
}
/**
* Set the default InstanceClassFactory. This is used when there is not
* match an exact match by the {@linkplain #getInstanceClassFactory(Class)}
* factory method.
*
* @param defaultInstanceFactory the default InstanceClassFactory to fall
* back to. It may be null if no default should be used.
*/
public void setDefaultInstanceFactory(
InstanceClassFactory<? extends Serializable> defaultInstanceFactory)
{
this.defaultInstanceFactory = defaultInstanceFactory;
}
public ManagedObjectPopulator<?> getDefaultManagedObjectPopulator()
{
return defaultManagedObjectPopulator;
}
/**
* A default implementation of ManagedObjectPopulator that is used when
* there is no ManagedObjectBuilder registered for a given type.
* @see #getBuilder(Class)
* @param defaultManagedObjectPopulator
*/
public void setDefaultManagedObjectPopulator(
ManagedObjectPopulator<? extends Serializable> defaultManagedObjectPopulator)
{
this.defaultManagedObjectPopulator = defaultManagedObjectPopulator;
}
@Override
public <T> ManagedObject createManagedObject(Class<T> clazz, MetaData metaData)
{
if (clazz == null)
throw new IllegalArgumentException("Null class");
ManagedObject result = createSkeletonManagedObject(clazz, metaData);
ManagedObjectPopulator<T> populator = getPopulator(clazz);
populator.createObject(result, clazz, metaData);
return result;
}
public ManagedObject initManagedObject(Object instance, Class<?> instanceType,
MetaData metaData, String name, String nameType)
{
if (instance == null)
throw new IllegalArgumentException("instance cannot be null");
Class<?> clazz = instance.getClass();
InstanceClassFactory icf = defaultInstanceFactory;
if(instanceType != null && instanceType != clazz)
icf = getInstanceClassFactory(instanceType, metaData);
if(icf == defaultInstanceFactory)
icf = getInstanceClassFactory(clazz, metaData);
Class<Object> moClass;
try
{
moClass = icf.getManagedObjectClass(instance);
}
catch(ClassNotFoundException e)
{
log.debug("Failed to load class for ManagedObject", e);
return null;
}
if(moClass == null)
{
log.debug("ICF returned null class: "+instance);
return null;
}
ManagedObject result = createSkeletonManagedObject(moClass, metaData);
if (result == null)
{
log.debug("Null ManagedObject created for: "+moClass);
return null;
}
log.debug("Created skeleton ManagedObject: "+result);
if(result instanceof MutableManagedObject)
{
MutableManagedObject mmo = (MutableManagedObject) result;
ManagedObjectPopulator<Object> populator = getPopulator(moClass);
populator.populateManagedObject(mmo, instance, metaData);
}
return result;
}
@Override
public void setBuilder(Class<?> clazz, ManagedObjectBuilder builder)
{
synchronized (builders)
{
if (builder == null)
builders.remove(clazz);
else
builders.put(clazz, builder);
}
}
@Override
public <T> void setInstanceClassFactory(Class<T> clazz, InstanceClassFactory<T> factory)
{
synchronized (instanceFactories)
{
if (factory == null)
{
instanceFactories.remove(clazz);
log.debug("Removed ICF for: "+clazz);
}
else
{
instanceFactories.put(clazz, factory);
log.debug("Set ICF for: "+clazz+", to: "+factory);
}
}
}
/**
* Create a skeleton managed object
*
* @param <T> the type
* @param clazz the clazz
* @return the skeleton managed object, null if clazz is not
* marked as a ManagementObject.
* {@linkplain ManagementObject}
*/
protected <T> ManagedObject createSkeletonManagedObject(Class<T> clazz, MetaData metaData)
{
if (clazz == null)
throw new IllegalArgumentException("Null class");
ManagedObjectBuilder builder = getBuilder(clazz);
return builder.buildManagedObject(clazz, metaData);
}
/**
* The ManagedObjectBuilder.buildManagedObject implementation. This is based
* on the org.jboss.managed.api.annotation.* package annotations.
* @param clazz the attachment class
* @param metaData - the optional metadata repository accessor used to query
* for management annotation overrides/additions to the clazz
* @return the ManagementObject if clazz is properly annotated, null if
* it does not have a ManagementObject annotation on the class or metaData.
* The BeanInfo used by this method is stored as a transient attachment
* under the BeanInfo.class.getName() ket.
*/
@SuppressWarnings("unchecked")
public ManagedObject buildManagedObject(Class<?> clazz, MetaData metaData)
{
boolean trace = log.isTraceEnabled();
BeanInfo beanInfo = configuration.getBeanInfo(clazz);
ClassInfo classInfo = beanInfo.getClassInfo();
ManagementObject managementObject = getAnnotation(ManagementObject.class, classInfo, metaData);
if( managementObject == null )
{
if (trace)
log.trace("No ManagementObject annotation, skipping ManagedObject for class: "+clazz);
// Skip the ManagedObject creation
return null;
}
// If a targetInterface exists, rebuild the BeanInfo from that
Class<?> targetInterface = managementObject.targetInterface();
if(targetInterface != Object.class)
{
beanInfo = getBeanInfo(targetInterface);
classInfo = beanInfo.getClassInfo();
}
HashMap<String, Annotation> moAnnotations = new HashMap<String, Annotation>();
moAnnotations.put(ManagementObject.class.getName(), managementObject);
ManagementDeployment mnagementDeployment = getAnnotation(ManagementDeployment.class, classInfo, metaData);
if(mnagementDeployment != null)
moAnnotations.put(ManagementDeployment.class.getName(), mnagementDeployment);
ManagementObjectID moID = getAnnotation(ManagementObjectID.class, classInfo, metaData);
if (moID != null)
moAnnotations.put(ManagementObjectID.class.getName(), moID);
// Process the ManagementObject fields
boolean isRuntime = managementObject.isRuntime();
String name = classInfo.getName();
String nameType = null;
String attachmentName = classInfo.getName();
Class<? extends Fields> moFieldsFactory = null;
ConstraintsPopulatorFactory moCPF = null;
Class<? extends ManagedPropertyConstraintsPopulatorFactory> moConstraintsFactory = null;
Class<? extends ManagedProperty> moPropertyFactory = null;
if (managementObject != null)
{
name = managementObject.name();
if (name.length() == 0 || name.equals(ManagementConstants.GENERATED))
name = classInfo.getName();
nameType = managementObject.type();
if (nameType.length() == 0)
nameType = null;
attachmentName = managementObject.attachmentName();
if (attachmentName.length() == 0)
attachmentName = classInfo.getName();
// Check for a component specification
ManagementComponent mc = null;
if (metaData != null )
mc = metaData.getAnnotation(ManagementComponent.class);
if (mc == null)
mc = managementObject.componentType();
// Work around JBMDR-51 by checking type/subtype
// if (mc.equals(AnnotationDefaults.COMP_TYPE) == false)
if (mc.type().length() > 0 || mc.subtype().length() > 0)
{
log.debug("ManagedObject("+name+") is ManagedComponent type: "+mc);
moAnnotations.put(ManagementComponent.class.getName(), mc);
}
// ManagementObject level default factory classes
FieldsFactory ff = getAnnotation(FieldsFactory.class, classInfo, metaData);
if(ff != null)
moFieldsFactory = ff.value();
moCPF = getAnnotation(ConstraintsPopulatorFactory.class, classInfo, metaData);
if(moCPF != null)
moConstraintsFactory = moCPF.value();
ManagementPropertyFactory mpf = getAnnotation(ManagementPropertyFactory.class, classInfo, metaData);
if(mpf != null)
moPropertyFactory = mpf.value();
}
if (trace)
{
log.trace("Building MangedObject(name="+name+",nameType="+nameType
+",attachmentName="+attachmentName+",isRuntime="+isRuntime+")");
}
ManagementProperties propertyType = ManagementProperties.ALL;
Set<String> classProperties = null;
if (managementObject != null)
{
propertyType = managementObject.properties();
if(propertyType == ManagementProperties.CLASS || propertyType == ManagementProperties.CLASS_AND_EXPLICIT)
{
classProperties = new HashSet<String>();
for(ManagementProperty mp : managementObject.classProperties())
{
if(mp.name().length() > 0)
classProperties.add(mp.name());
if(mp.mappedName().length() > 0)
classProperties.add(mp.mappedName());
}
}
}
// Build the ManagedProperties
Set<ManagedProperty> properties = new HashSet<ManagedProperty>();
Set<PropertyInfo> propertyInfos = beanInfo.getProperties();
if (propertyInfos != null && propertyInfos.isEmpty() == false)
{
for (PropertyInfo propertyInfo : propertyInfos)
{
// Ignore the "class" property
if ("class".equals(propertyInfo.getName()))
continue;
ManagementProperty managementProperty = getAnnotation(ManagementProperty.class, propertyInfo, metaData);
ManagementObjectID id = getAnnotation(ManagementObjectID.class, propertyInfo, metaData);
ManagementObjectRef ref = getAnnotation(ManagementObjectRef.class, propertyInfo, metaData);
ManagementRuntimeRef runtimeRef = getAnnotation(ManagementRuntimeRef.class, propertyInfo, metaData);
RunStateProperty rsp = getAnnotation(RunStateProperty.class, propertyInfo, metaData);
Masked masked = getAnnotation(Masked.class, propertyInfo, metaData);
DefaultValueBuilderFactory defaultsFactory = getAnnotation(DefaultValueBuilderFactory.class, propertyInfo, metaData);
HashMap<String, Annotation> propAnnotations = new HashMap<String, Annotation>();
if (managementProperty != null)
propAnnotations.put(ManagementProperty.class.getName(), managementProperty);
if (id != null)
{
propAnnotations.put(ManagementObjectID.class.getName(), id);
// This overrides the MO nameType
nameType = id.type();
}
if (ref != null)
propAnnotations.put(ManagementObjectRef.class.getName(), ref);
if (runtimeRef != null)
propAnnotations.put(ManagementRuntimeRef.class.getName(), runtimeRef);
if (rsp != null)
propAnnotations.put(RunStateProperty.class.getName(), rsp);
if (masked != null)
propAnnotations.put(Masked.class.getName(), masked);
// Check whether this property should be included
boolean includeProperty = false;
switch(propertyType)
{
// Only if the property as a ManagementProperty
case EXPLICIT:
includeProperty = managementProperty != null &&
(managementProperty.ignored() == false);
break;
// Only if the property is listed in the classProperties
case CLASS:
includeProperty = classProperties.contains(propertyInfo.getName());
break;
// Only if the property is listed in the classProperties
case CLASS_AND_EXPLICIT:
includeProperty = classProperties.contains(propertyInfo.getName())
|| (managementProperty != null && managementProperty.ignored() == false);
break;
// Any property that is not ignored
case ALL:
includeProperty = managementProperty == null
|| managementProperty.ignored() == false;
break;
}
if (includeProperty)
{
Fields fields = null;
Class<? extends Fields> factory = moFieldsFactory;
FieldsFactory ff = getAnnotation(FieldsFactory.class, propertyInfo, metaData);
if(ff != null)
factory = ff.value();
if (factory != null)
{
try
{
fields = factory.newInstance();
}
catch (Exception e)
{
log.debug("Failed to created Fields", e);
}
}
if (fields == null)
fields = new DefaultFieldsImpl();
if( propertyInfo instanceof Serializable )
{
Serializable info = Serializable.class.cast(propertyInfo);
fields.setField(Fields.PROPERTY_INFO, info);
}
String propertyName = propertyInfo.getName();
if (managementProperty != null)
propertyName = managementProperty.name();
if( propertyName.length() == 0 )
propertyName = propertyInfo.getName();
fields.setField(Fields.NAME, propertyName);
// This should probably always the the propertyInfo name?
String mappedName = propertyInfo.getName();
if (managementProperty != null)
mappedName = managementProperty.mappedName();
if( mappedName.length() == 0 )
mappedName = propertyInfo.getName();
fields.setField(Fields.MAPPED_NAME, mappedName);
String description = ManagementConstants.GENERATED;
if (managementProperty != null)
description = managementProperty.description();
if (description.equals(ManagementConstants.GENERATED))
description = propertyName;
fields.setField(Fields.DESCRIPTION, description);
if (trace)
{
log.trace("Building MangedProperty(name="+propertyName
+",mappedName="+mappedName
+") ,annotations="+propAnnotations);
}
boolean mandatory = false;
if (managementProperty != null)
mandatory = managementProperty.mandatory();
if (mandatory)
fields.setField(Fields.MANDATORY, Boolean.TRUE);
boolean readOnly = propertyInfo.isWritable() == false;
if (readOnly == false && managementProperty != null)
readOnly = managementProperty.readOnly();
if (readOnly)
fields.setField(Fields.READ_ONLY, Boolean.TRUE);
boolean managed = false;
if (managementProperty != null)
managed = managementProperty.managed();
// View Use
if (managementProperty != null)
{
ViewUse[] use = managementProperty.use();
fields.setField(Fields.VIEW_USE, use);
}
// ActivationPolicy
ActivationPolicy apolicy = ActivationPolicy.IMMEDIATE;
if (managementProperty != null)
{
apolicy = managementProperty.activationPolicy();
}
fields.setField(Fields.ACTIVATION_POLICY, apolicy);
// The managed property type
MetaMapper[] mapperReturn = {null};
MetaType metaType = this.getMetaType(propertyInfo, propertyInfo.getType(), metaData, false, mapperReturn);
// Determine meta type based on property type
if(metaType == null)
{
if (managed)
{
TypeInfo typeInfo = propertyInfo.getType();
if(typeInfo.isArray())
metaType = new ArrayMetaType(1, MANAGED_OBJECT_META_TYPE);
else if (typeInfo.isCollection())
metaType = new CollectionMetaType(typeInfo.getName(), MANAGED_OBJECT_META_TYPE);
else
metaType = MANAGED_OBJECT_META_TYPE;
}
else
{
metaType = metaTypeFactory.resolve(propertyInfo.getType());
}
}
fields.setField(Fields.META_TYPE, metaType);
// Default value
if(managementProperty != null)
{
String defaultValue = managementProperty.defaultValue();
if(defaultValue.length() > 0)
{
try
{
// Check for a DefaultValueBuilderFactory
DefaultValueBuilder builder = null;
if(defaultsFactory != null)
{
Class<? extends DefaultValueBuilder> factoryClass = defaultsFactory.value();
builder = factoryClass.newInstance();
}
// Lookup the builder by metaType
if(builder == null)
{
builder = defaultBuilders.get(metaType);
}
if(builder != null)
{
MetaValue defaultMV = builder.buildMetaValue(defaultValue);
if(defaultMV != null)
fields.setField(Fields.DEFAULT_VALUE, defaultMV);
}
else
{
log.warn("Failed to find DefaultValueBuilder for type: "+metaType);
}
}
catch(Exception e)
{
log.warn("Failed to create default value for: "+propertyInfo, e);
}
}
}
// Property annotations
if (propAnnotations.isEmpty() == false)
fields.setField(Fields.ANNOTATIONS, propAnnotations);
// Delegate others (legal values, min/max etc.) to the constraints factory
try
{
Class<? extends ManagedPropertyConstraintsPopulatorFactory> factoryClass = moConstraintsFactory;
ConstraintsPopulatorFactory cpf = getAnnotation(ConstraintsPopulatorFactory.class, propertyInfo, metaData);
if(cpf != null)
factoryClass = cpf.value();
else
cpf = moCPF;
if(factoryClass != null)
{
ManagedPropertyConstraintsPopulatorFactory mpcpf = factoryClass.newInstance();
ManagedPropertyConstraintsPopulator populator = mpcpf.newInstance(cpf.min(), cpf.max(), cpf.legalValues(), cpf.args());
if (populator != null)
populator.populateManagedProperty(clazz, propertyInfo, fields);
}
}
catch(Exception e)
{
log.debug("Failed to populate constraints for: "+propertyInfo, e);
}
ManagedProperty property = null;
Class<? extends ManagedProperty> mpClass = moPropertyFactory;
ManagementPropertyFactory mpf = getAnnotation(ManagementPropertyFactory.class, propertyInfo, metaData);
if (mpf != null)
mpClass = mpf.value();
if (mpClass != null)
property = getManagedProperty(mpClass, fields);
// we should have write-through by default
// use factory to change this default behavior
if (property == null)
property = createDefaultManagedProperty(fields);
// Pass the MetaMapper as an attachment
if (mapperReturn[0] != null)
property.setTransientAttachment(MetaMapper.class.getName(), mapperReturn[0]);
properties.add(property);
}
else if (trace)
log.trace("Ignoring property: " + propertyInfo);
}
}
/* TODO: Operations. In general the bean metadata does not contain
operation information.
*/
Set<ManagedOperation> operations = new HashSet<ManagedOperation>();
Set<MethodInfo> methodInfos = beanInfo.getMethods();
if (methodInfos != null && methodInfos.isEmpty() == false)
{
for (MethodInfo methodInfo : methodInfos)
{
ManagementOperation managementOp = getAnnotation(ManagementOperation.class, methodInfo, metaData);
if (managementOp == null)
continue;
ManagedOperation op = getManagedOperation(methodInfo, managementOp, metaData);
operations.add(op);
}
}
ManagedObjectImpl result = new ManagedObjectImpl(name, properties);
result.setAnnotations(moAnnotations);
// Set the component name to name if this is a runtime MO with a name specified
if (isRuntime && name.equals(classInfo.getName()) == false)
result.setComponentName(name);
if (nameType != null)
result.setNameType(nameType);
if (attachmentName != null)
result.setAttachmentName(attachmentName);
if (operations.size() > 0 )
result.setOperations(operations);
for (ManagedProperty property : properties)
property.setManagedObject(result);
result.setTransientAttachment(BeanInfo.class.getName(), beanInfo);
return result;
}
/**
* Create default MangedProperty instance.
* Override this method for different default.
*
* @param fields the fields
* @return new ManagedProperty instance
*/
protected ManagedProperty createDefaultManagedProperty(Fields fields)
{
return new WritethroughManagedPropertyImpl(fields, metaValueFactory, this);
}
/**
* Get the property name.
*
* @param property managed property
* @return property name
*/
protected String getPropertyName(ManagedProperty property)
{
// First look to the mapped name
String name = property.getMappedName();
if (name == null)
property.getName();
return name;
}
/**
*
* @param methodInfo
* @param opAnnotation
* @return the managed operation
*/
protected ManagedOperation getManagedOperation(MethodInfo methodInfo,
ManagementOperation opAnnotation, MetaData metaData)
{
String name = methodInfo.getName();
String description = opAnnotation.description();
Impact impact = opAnnotation.impact();
ManagementParameter[] params = opAnnotation.params();
ParameterInfo[] paramInfo = methodInfo.getParameters();
ArrayList<ManagedParameter> mparams = new ArrayList<ManagedParameter>();
Class<? extends ManagedParameterConstraintsPopulatorFactory> opConstraintsFactor = opAnnotation.constraintsFactory();
// The op return type
MetaMapper[] returnTypeMapper = {null};
MetaType returnType = getMetaType(methodInfo, methodInfo.getReturnType(), metaData, true, returnTypeMapper);
// Process the op parameters
if( paramInfo != null )
{
for(int i = 0; i < paramInfo.length; i ++)
{
ParameterInfo pinfo = paramInfo[i];
String pname = pinfo.getName();
String pdescription = null;
ManagementParameter mpa = null;
// Look to ManagementParameter for info
if (i < params.length)
{
mpa = params[i];
if (mpa.name().equals(AnnotationDefaults.EMPTY_STRING) == false)
pname = mpa.name();
if (mpa.description().equals(AnnotationDefaults.EMPTY_STRING) == false)
pdescription = mpa.description();
}
// Generate a name if there is none
if (pname == null)
pname = "arg#" + i;
Fields fields = new DefaultFieldsImpl(pname);
if (pdescription != null)
fields.setField(Fields.DESCRIPTION, pdescription);
MetaMapper[] paramMapper = {null};
MetaType metaType = getMetaType(pinfo, pinfo.getParameterType(), metaData, true, paramMapper);
fields.setField(Fields.META_TYPE, metaType);
// Delegate others (legal values, min/max etc.) to the constraints factory
try
{
Class<? extends ManagedParameterConstraintsPopulatorFactory> factoryClass = opConstraintsFactor;
if (factoryClass == ManagementParameter.NULL_CONSTRAINTS.class)
{
if (mpa != null)
factoryClass = mpa.constraintsFactory();
}
ManagedParameterConstraintsPopulatorFactory factory = factoryClass.newInstance();
ManagedParameterConstraintsPopulator populator = factory.newInstance();
if (populator != null)
populator.populateManagedParameter(name, pinfo, fields);
}
catch(Exception e)
{
log.debug("Failed to populate constraints for: "+pinfo, e);
}
ManagedParameterImpl mp = new ManagedParameterImpl(fields);
if(paramMapper[0] != null)
mp.setTransientAttachment(MetaMapper.class.getName(), paramMapper[0]);
mparams.add(mp);
}
}
ManagedParameter[] parameters = new ManagedParameter[mparams.size()];
mparams.toArray(parameters);
ManagedOperationImpl op = new ManagedOperationImpl(name, description, impact, parameters, returnType);
if(returnTypeMapper[0] != null)
op.setTransientAttachment(MetaMapper.class.getName(), returnTypeMapper[0]);
return op;
}
/**
* Get the MetaType for info by looking for MetaMapping/MetaMappingFactory
* annotations in addition to the info type.
*
* @param methodInfo
* @param metaData
* @return the MetaType for info's type
*/
protected MetaType getMetaType(AnnotatedInfo info, TypeInfo infoType, MetaData metaData,
boolean useTypeFactory, MetaMapper[] mapperReturn)
{
MetaType returnType = null;
// First look for meta mappings
MetaMapper<?> metaMapper = null;
MetaMapping metaMapping = getAnnotation(MetaMapping.class, info, metaData);
MetaMappingFactory metaMappingFactory = getAnnotation(MetaMappingFactory.class, info, metaData);
if(metaMappingFactory != null)
{
Class<? extends MetaMapperFactory<?>> mmfClass = metaMappingFactory.value();
try
{
MetaMapperFactory<?> mmf = mmfClass.newInstance();
String[] args = metaMappingFactory.args();
if(args.length > 0)
metaMapper = mmf.newInstance(args);
else
metaMapper = mmf.newInstance();
}
catch(Exception e)
{
log.debug("Failed to create MetaMapperFactory: "+metaMappingFactory, e);
}
}
if(metaMapping != null)
{
// Use the mapping for the type
Class<? extends MetaMapper<?>> mapperClass = metaMapping.value();
try
{
metaMapper = mapperClass.newInstance();
}
catch(Exception e)
{
log.debug("Failed to create MetaMapper: "+metaMapping, e);
}
}
if(metaMapper != null)
{
returnType = metaMapper.getMetaType();
// Return the MetaMapper
if(mapperReturn != null && mapperReturn.length > 0)
mapperReturn[0] = metaMapper;
}
if(returnType == null && useTypeFactory)
{
// Use the type factory to convert the info type
returnType = metaTypeFactory.resolve(infoType);
}
return returnType;
}
/**
* Get the builder for a class
*
* @param clazz the class
* @return the builder
*/
protected ManagedObjectBuilder getBuilder(Class<?> clazz)
{
synchronized (builders)
{
ManagedObjectBuilder builder = builders.get(clazz);
if (builder != null)
return builder;
}
return this;
}
/**
* Get the instance factory for a class
*
* @param clazz the class
* @return the InstanceClassFactory
*/
@SuppressWarnings("unchecked")
public <X> InstanceClassFactory<X> getInstanceClassFactory(Class<X> clazz,
MetaData metaData)
{
InstanceClassFactory defaultFactory = defaultInstanceFactory;
if(metaData != null)
{
InstanceClassFactory mdrFactory = metaData.getMetaData(InstanceClassFactory.class);
if(mdrFactory != null)
defaultFactory = mdrFactory;
}
InstanceClassFactory<X> factory = (InstanceClassFactory<X>)
Utility.getInstanceClassFactory(clazz, instanceFactories,
defaultFactory);
return factory;
}
/**
* Initialize the DefaultValueBuilder map with known implementations
*/
protected void initDefaultValueBuilders()
{
addDefaultValueBuilder(new BigDecimalDefaultValueBuilder());
addDefaultValueBuilder(new BigIntegerDefaultValueBuilder());
addDefaultValueBuilder(new DoubleDefaultValueBuilder());
addDefaultValueBuilder(new DoublePrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new FloatDefaultValueBuilder());
addDefaultValueBuilder(new FloatPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new CharDefaultValueBuilder());
addDefaultValueBuilder(new CharPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new IntDefaultValueBuilder());
addDefaultValueBuilder(new IntPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new ShortDefaultValueBuilder());
addDefaultValueBuilder(new ShortPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new LongDefaultValueBuilder());
addDefaultValueBuilder(new LongPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new ByteDefaultValueBuilder());
addDefaultValueBuilder(new BytePrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new BooleanDefaultValueBuilder());
addDefaultValueBuilder(new BooleanPrimitiveDefaultValueBuilder());
addDefaultValueBuilder(new PropertiesDefaultValueBuilder());
addDefaultValueBuilder(new StringDefaultValueBuilder());
}
/**
* Get the populator for a class
*
* @param clazz the class
* @return the populator
*/
@SuppressWarnings("unchecked")
protected <X> ManagedObjectPopulator<X> getPopulator(Class<X> clazz)
{
ManagedObjectBuilder builder = getBuilder(clazz);
if (builder instanceof ManagedObjectPopulator)
return (ManagedObjectPopulator) builder;
ManagedObjectPopulator<X> mop = (ManagedObjectPopulator<X>) defaultManagedObjectPopulator;
return mop;
}
protected Collection<?> getAsCollection(Object value)
{
if( value.getClass().isArray() )
return Arrays.asList(value);
else if (value instanceof Collection)
return Collection.class.cast(value);
return null;
}
/**
* Look for ctor(Fields)
* @param factory - the ManagedProperty implementation class
* @param fields - the fields to pass to the ctor
* @return the managed property
*/
protected ManagedProperty getManagedProperty(Class<? extends ManagedProperty> factory, Fields fields)
{
return createManagedProperty(factory, fields);
}
protected <X extends Annotation> X getAnnotation(Class<X> annotationType,
AnnotatedInfo info, MetaData metaData)
{
X annotation = null;
if(metaData != null)
{
annotation = metaData.getAnnotation(annotationType);
if(annotation == null && info instanceof MethodInfo)
{
MethodInfo mi = (MethodInfo) info;
Signature mis = Signature.getSignature(mi);
MetaData imetaData = metaData.getComponentMetaData(mis);
if (imetaData != null)
annotation = imetaData.getAnnotation(annotationType);
}
if (annotation == null && info instanceof PropertyInfo)
{
PropertyInfo pi = (PropertyInfo) info;
if(pi.getGetter() != null)
{
Signature mis = new MethodSignature(pi.getGetter());
MetaData imetaData = metaData.getComponentMetaData(mis);
if (imetaData != null)
annotation = imetaData.getAnnotation(annotationType);
}
if(annotation == null && pi.getSetter() != null)
{
Signature mis = new MethodSignature(pi.getSetter());
MetaData imetaData = metaData.getComponentMetaData(mis);
if (imetaData != null)
annotation = imetaData.getAnnotation(annotationType);
}
if(annotation == null && pi.getFieldInfo() != null)
{
Signature fis = new FieldSignature(pi.getFieldInfo());
MetaData imetaData = metaData.getComponentMetaData(fis);
if (imetaData != null)
annotation = imetaData.getAnnotation(annotationType);
}
}
if(annotation != null)
log.trace("Loaded "+annotationType+" from MetaData");
}
if(annotation == null)
annotation = info.getUnderlyingAnnotation(annotationType);
return annotation;
}
/**
* Build up a BeanInfo from the interface class and all interfaces it
* implements.
*
* @param iface - the interface for the managed object
* @return the full BeanInfo for the iface
*/
protected BeanInfo getBeanInfo(Class<?> iface)
{
BeanInfo ifaceBI = configuration.getBeanInfo(iface);
Class<?>[] superIfaces = iface.getInterfaces();
if(superIfaces != null && superIfaces.length > 0)
{
// Combine all properties
Set<PropertyInfo> allProps = new HashSet<PropertyInfo>(ifaceBI.getProperties());
// Combine all operations
Set<MethodInfo> allMethods = new HashSet<MethodInfo>(ifaceBI.getMethods());
for(Class<?> superIface : superIfaces)
{
BeanInfo cBI = configuration.getBeanInfo(superIface);
Set<PropertyInfo> props = cBI.getProperties();
if(props != null)
allProps.addAll(props);
Set<MethodInfo> methods = cBI.getMethods();
if(methods != null)
allMethods.addAll(methods);
}
ifaceBI.setProperties(allProps);
ifaceBI.setMethods(allMethods);
}
return ifaceBI;
}
}