/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.profileservice.management.views;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.jboss.logging.Logger;
import org.jboss.managed.api.ComponentType;
import org.jboss.managed.api.DeploymentState;
import org.jboss.managed.api.ManagedComponent;
import org.jboss.managed.api.ManagedDeployment;
import org.jboss.managed.api.ManagedObject;
import org.jboss.managed.api.ManagedOperation;
import org.jboss.managed.api.ManagedProperty;
import org.jboss.managed.api.MutableManagedObject;
import org.jboss.managed.api.RunState;
import org.jboss.managed.api.annotation.ManagementComponent;
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.plugins.ManagedComponentImpl;
import org.jboss.managed.plugins.ManagedDeploymentImpl;
import org.jboss.managed.plugins.factory.AbstractManagedObjectFactory;
import org.jboss.metatype.api.types.ArrayMetaType;
import org.jboss.metatype.api.types.CollectionMetaType;
import org.jboss.metatype.api.types.MetaType;
import org.jboss.metatype.api.values.ArrayValue;
import org.jboss.metatype.api.values.CollectionValue;
import org.jboss.metatype.api.values.GenericValue;
import org.jboss.metatype.api.values.MetaValue;
import org.jboss.metatype.api.values.SimpleValue;
import org.jboss.profileservice.spi.NoSuchDeploymentException;
/**
* A abstract managed deployment view.
*
* @author Scott.Stark@jboss.org
* @author adrian@jboss.org
* @author ales.justin@jboss.org
* @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
*
* @version $Revision$
*/
public abstract class AbstractManagedDeploymentView
{
/** The logger. */
private static final Logger log = Logger.getLogger(AbstractManagedDeploymentView.class);
/** An index of ManagedComponent by ComponentType */
private Map<ComponentType, Set<ManagedComponent>> compByCompType = new HashMap<ComponentType, Set<ManagedComponent>>();
/** id/type key to ManagedObject map */
private Map<String, ManagedObject> moRegistry = new HashMap<String, ManagedObject>();
/** The ManagedPropertys with unresolved ManagementObjectRefs */
private Map<String, Set<ManagedProperty>> unresolvedRefs = new HashMap<String, Set<ManagedProperty>>();
/** A map of runtime ManagedObjects needing to be merged with their matching ManagedObject. */
private Map<String, ManagedObject> runtimeMOs = new HashMap<String, ManagedObject>();
/** The deployment name to ManagedDeployment map */
private Map<String, ManagedDeployment> managedDeployments = new TreeMap<String, ManagedDeployment>();
/** The root deployments to resolve the deployment name. */
private List<String> rootDeployments = new ArrayList<String>();
/**
* Process managed deployment.
*
* @param md the managed deployment
* @param profile the associated profile key
* @param state the deployment state
* @param level depth level
* @param trace is trace enabled
* @throws Exception for any error
*/
protected void processManagedDeployment(ManagedDeployment md, DeploymentState state, int level, boolean trace) throws Exception
{
String name = md.getName();
if (trace)
log.trace(name + " ManagedDeployment_" + level + ": " + md);
Map<String, ManagedObject> mos = md.getManagedObjects();
if (trace)
log.trace(name + " ManagedObjects_ " + level + ": " + mos);
// Set the deployment state
if(state != null && md instanceof ManagedDeploymentImpl)
((ManagedDeploymentImpl)md).setDeploymentState(state);
for(ManagedObject mo : mos.values())
{
processManagedObject(mo, md);
}
managedDeployments.put(name, md);
// Add root deployments
if(level == 0)
this.rootDeployments.add(name);
// Process children
List<ManagedDeployment> mdChildren = md.getChildren();
if(mdChildren != null && mdChildren.isEmpty() == false)
{
for(ManagedDeployment mdChild : mdChildren)
{
// process the child deployments, with the state of the parent.
processManagedDeployment(mdChild, state, level + 1, trace);
}
}
}
/**
* Get the deployment names for a given type.
*
* @param type the deployment type
* @return the matching deployments
*/
public Set<String> getDeploymentNamesForType(String type)
{
boolean trace = log.isTraceEnabled();
HashSet<String> matches = new HashSet<String>();
for(ManagedDeployment md : managedDeployments.values())
{
String name = md.getName();
Set<String> types = md.getTypes();
if(types != null)
{
if(types.contains(type))
{
if(trace)
log.trace(name+" matches type: "+type+", types:"+types);
matches.add(name);
}
}
}
return matches;
}
/**
* Get the deployments for a given type.
*
* @param type the deployment type.
* @return the matching deployments
*/
public Set<ManagedDeployment> getDeploymentsForType(String type)
{
Set<String> names = getDeploymentNamesForType(type);
HashSet<ManagedDeployment> mds = new HashSet<ManagedDeployment>();
for(String name : names)
{
ManagedDeployment md = this.managedDeployments.get(name);
mds.add(md);
}
return mds;
}
public Set<ComponentType> getComponentTypes()
{
return compByCompType.keySet();
}
/**
* Get component for a given type.
*
* @param type the component type
* @return a set of matching components
*/
public Set<ManagedComponent> getComponentsForType(ComponentType type)
{
Set<ManagedComponent> comps = compByCompType.get(type);
if(comps == null)
comps = Collections.emptySet();
return comps;
}
/**
* Get the deployment names.
*
* @return the deployment names
*/
public Set<String> getDeploymentNames()
{
return this.managedDeployments.keySet();
}
/**
* Find a deployment.
*
* @param name
* @return
* @throws NoSuchDeploymentException
*/
public Collection<ManagedDeployment> getDeployment(String name) throws NoSuchDeploymentException
{
if(name == null)
throw new IllegalArgumentException("Null deployment name");
List<ManagedDeployment> deployments = new ArrayList<ManagedDeployment>();
// Check the file name
if(this.managedDeployments.containsKey(name))
{
ManagedDeployment md = this.managedDeployments.get(name);
if(md != null)
deployments.add(md);
}
else
{
// Look for a simple name
for(String deployment : this.rootDeployments)
{
String fixedDeploymentName = deployment;
if(deployment.endsWith("/"))
fixedDeploymentName = deployment.substring(0, deployment.length() - 1);
if(fixedDeploymentName.endsWith(name))
{
ManagedDeployment md = this.managedDeployments.get(deployment);
if(md != null)
deployments.add(md);
}
}
}
return deployments;
}
protected abstract void mergeRuntimeMO(ManagedObject mo, ManagedObject runtimeMO) throws Exception;
protected abstract Set<ManagedOperation> createOperationProxies(ManagedObject mo, Set<ManagedOperation> runtimeOps) throws Exception;
protected abstract RunState updateRunState(ManagedObject mo, ManagedComponent comp) throws Exception;
/**
* Process managed object.
*
* @param mo the managed object
* @param md the managed deployment
*/
protected void processManagedObject(ManagedObject mo, ManagedDeployment md)
throws Exception
{
String key = mo.getName() + "/" + mo.getNameType();
if(mo.getName().equals("org.jboss.security.plugins.SecurityConfig"))
log.info("Saw SecurityConfig MO");
log.debug("ID for ManagedObject: "+key+", attachmentName: "+mo.getAttachmentName());
// See if this is a runtime ManagedObject
Map<String, Annotation> moAnns = mo.getAnnotations();
ManagementObject managementObject = (ManagementObject) moAnns.get(ManagementObject.class.getName());
if (managementObject.isRuntime())
{
boolean merged = false;
ManagementComponent mc = managementObject.componentType();
boolean isMC = !(mc.type().length() == 0 && mc.subtype().length() == 0);
// Merge this with the ManagedObject
ManagedObject parentMO = moRegistry.get(key);
if (parentMO == null && isMC == false)
{
log.debug("Deferring resolution of runtime ManagedObject: "+managementObject);
// Save the runtime mo for merging
runtimeMOs.put(key, mo);
}
else
{
mergeRuntimeMO(parentMO, mo);
merged = true;
runtimeMOs.remove(key);
}
// Update the runtime state of any ManagedComponent associated with this runtime mo
ManagedComponent comp = md.getComponent(mo.getName());
if (comp != null)
{
RunState state = updateRunState(mo, comp);
log.debug("Updated component: "+comp+" run state to: "+state);
}
// There is no further processing of runtime ManagedObjects, unless its marked as a component
if (isMC == false)
return;
//
else if (merged == false)
{
Set<ManagedOperation> runtimeOps = mo.getOperations();
runtimeOps = createOperationProxies(mo, runtimeOps);
MutableManagedObject moi = (MutableManagedObject) mo;
moi.setOperations(runtimeOps);
}
}
else
{
// See if there is runtime info to merge
ManagedObject runtimeMO = runtimeMOs.get(key);
if (runtimeMO != null)
{
mergeRuntimeMO(mo, runtimeMO);
runtimeMOs.remove(key);
// Update the runtime state of any ManagedComponent associated with this runtime mo
ManagedComponent comp = md.getComponent(mo.getName());
if (comp != null)
{
RunState state = updateRunState(runtimeMO, comp);
log.debug("Updated component: "+comp+" run state to: "+state);
}
}
}
// Update the MO registry
// TODO - does this make sense? In case of a MetaType.isCollection we could get different results then
// ManagedObject prevMO = moRegistry.put(key, mo);
// if( prevMO != null )
// {
// // This should only matter for ManagedObjects that have a ManagementObjectID
// log.debug("Duplicate mo for key: "+key+", prevMO: "+prevMO);
// return;
// }
// Check for unresolved refs
checkForReferences(key, mo);
// Map any existing ManagedComponent types
for(ManagedComponent comp : md.getComponents().values())
{
log.debug("Updating ManagementComponent: "+comp);
ComponentType type = comp.getType();
Set<ManagedComponent> typeComps = compByCompType.get(type);
if (typeComps == null)
{
typeComps = new HashSet<ManagedComponent>();
compByCompType.put(type, typeComps);
}
typeComps.add(comp);
}
// Create ManagedComponents for ManagedObjects annotated with ManagementComponent
ManagementComponent mc = (ManagementComponent) moAnns.get(ManagementComponent.class.getName());
if (mc != null && md.getComponent(mo.getName()) == null)
{
ComponentType type = new ComponentType(mc.type(), mc.subtype());
ManagedComponentImpl comp = new ManagedComponentImpl(type, md, mo);
md.addComponent(mo.getName(), comp);
log.debug("Processing ManagementComponent("+mo.getName()+"): "+comp);
Set<ManagedComponent> typeComps = compByCompType.get(type);
if (typeComps == null)
{
typeComps = new HashSet<ManagedComponent>();
compByCompType.put(type, typeComps);
}
typeComps.add(comp);
updateRunState(null, comp);
}
// Scan for @ManagementObjectRef
for(ManagedProperty prop : mo.getProperties().values())
{
log.debug("Checking property: "+prop);
// See if this is a ManagementObjectID
Map<String, Annotation> pannotations = prop.getAnnotations();
if (pannotations != null && pannotations.isEmpty() == false)
{
ManagementObjectID id = (ManagementObjectID) pannotations.get(ManagementObjectID.class.getName());
if (id != null)
{
Object refName = getRefName(prop.getValue());
if (refName == null)
refName = id.name();
String propKey = refName + "/" + id.type();
log.debug("ManagedProperty level ID for ManagedObject: "+propKey+", attachmentName: "+mo.getAttachmentName());
moRegistry.put(propKey, mo);
checkForReferences(propKey, mo);
}
// See if this is a ManagementObjectRef
ManagementObjectRef ref = (ManagementObjectRef) pannotations.get(ManagementObjectRef.class.getName());
if ( ref != null )
{
// The reference key is the prop value + ref.type()
log.debug("Property("+prop.getName()+") references: "+ref);
Object refName = getRefName(prop.getValue());
if (refName == null)
refName = ref.name();
String targetKey = refName + "/" + ref.type();
ManagedObject target = moRegistry.get(targetKey);
if (target != null)
{
log.debug("Resolved property("+prop.getName()+") reference to: "+targetKey);
prop.setTargetManagedObject(target);
}
else
{
Set<ManagedProperty> referers = unresolvedRefs.get(targetKey);
if (referers == null)
{
referers = new HashSet<ManagedProperty>();
unresolvedRefs.put(targetKey, referers);
}
referers.add(prop);
}
}
}
MetaType propType = prop.getMetaType();
if (propType == AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE)
{
processGenericValue ((GenericValue)prop.getValue(), md);
}
else if (propType.isArray())
{
ArrayMetaType amt = (ArrayMetaType) propType;
MetaType etype = amt.getElementType();
if (etype == AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE)
{
ArrayValue avalue = (ArrayValue) prop.getValue();
int length = avalue != null ? avalue.getLength() : 0;
for(int n = 0; n < length; n ++)
processGenericValue((GenericValue) avalue.getValue(n), md);
}
}
else if (propType.isCollection())
{
CollectionMetaType amt = (CollectionMetaType) propType;
MetaType etype = amt.getElementType();
if (etype == AbstractManagedObjectFactory.MANAGED_OBJECT_META_TYPE)
{
CollectionValue avalue = (CollectionValue) prop.getValue();
if(avalue != null)
{
MetaValue[] elements = avalue.getElements();
for(int n = 0; n < avalue.getSize(); n ++)
{
GenericValue gv = (GenericValue) elements[n];
ManagedObject propMO = (ManagedObject) gv.getValue();
if(propMO != null)
processManagedObject(propMO, md);
}
}
}
}
}
}
/**
* Get ref name.
*
* @param value property value
* @return plain value
*/
protected Object getRefName(Object value)
{
if (value instanceof MetaValue)
{
MetaValue metaValue = (MetaValue)value;
if (metaValue.getMetaType().isSimple() == false)
throw new IllegalArgumentException("Can only get ref from simple value: " + value);
SimpleValue svalue = (SimpleValue) metaValue;
return svalue.getValue();
}
return value;
}
/**
* Check for references.
*
* @param key the property key
* @param mo the managed object
*/
protected void checkForReferences(String key, ManagedObject mo)
{
Set<ManagedProperty> referers = unresolvedRefs.get(key);
log.debug("checkForReferences, "+key+" has referers: "+referers);
if (referers != null)
{
for(ManagedProperty prop : referers)
{
prop.setTargetManagedObject(mo);
}
unresolvedRefs.remove(key);
}
}
/**
* Process generic value.
*
* @param genericValue the generic value
* @param md the managed deployment
* @throws Exception for any error
*/
protected void processGenericValue(GenericValue genericValue, ManagedDeployment md) throws Exception
{
// TODO: a null is probably an error condition
if (genericValue != null)
{
ManagedObject propMO = (ManagedObject) genericValue.getValue();
// TODO: a null is probably an error condition
if (propMO != null)
processManagedObject(propMO, md);
}
}
}