/*
* RHQ Management Platform
* Copyright (C) 2005-2009 Red Hat, Inc.
* All rights reserved.
*
* 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 version 2 of the License.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.xmlschema;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.rhq.core.clientapi.agent.metadata.ConfigurationMetadataParser;
import org.rhq.core.clientapi.agent.metadata.InvalidPluginDescriptorException;
import org.rhq.core.clientapi.descriptor.configuration.ConfigurationDescriptor;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.ConfigurationTemplate;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionList;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.enterprise.server.xmlschema.generated.serverplugin.ControlType;
import org.rhq.enterprise.server.xmlschema.generated.serverplugin.ServerPluginComponentType;
import org.rhq.enterprise.server.xmlschema.generated.serverplugin.ServerPluginDescriptorType;
/**
* Provides methods to parse the server plugin descriptors.
*
* @author John Mazzitelli
*/
public class ServerPluginDescriptorMetadataParser {
// pre-defined property names for schedule definitions
private static final String SCHEDULED_JOB_PROP_NAME_ENABLED = "enabled";
private static final String SCHEDULED_JOB_PROP_NAME_CLASS = "class";
private static final String SCHEDULED_JOB_PROP_NAME_METHOD_NAME = "methodName";
private static final String SCHEDULED_JOB_PROP_NAME_CONCURRENT = "concurrent";
private static final String SCHEDULED_JOB_PROP_NAME_CLUSTERED = "clustered";
private static final String SCHEDULED_JOB_PROP_NAME_SCHEDULE_TYPE = "scheduleType";
private static final String SCHEDULED_JOB_PROP_NAME_SCHEDULE_TRIGGER = "scheduleTrigger";
/**
* Returns the fully qualified class name of the plugin component.
* If the descriptor did not define a plugin component, this will return <code>null</code>.
* @param descriptor
*
* @return the name of the plugin component class, or <code>null</code> if not specified
*/
public static String getPluginComponentClassName(ServerPluginDescriptorType descriptor) {
ServerPluginComponentType componentXml = descriptor.getPluginComponent();
if (componentXml == null) {
return null;
}
String className = componentXml.getClazz();
if (className == null) {
// this should never happen, the xml schema validation should have caught this earlier
throw new IllegalArgumentException("Missing plugin component classname for plugin " + descriptor.getName());
}
className = getFullyQualifiedClassName(descriptor, className);
return className;
}
/**
* Given a plugn descriptor that may or may not have defined a {@link ServerPluginDescriptorType#getPackage() package name},
* this converts the given class name to a fully qualified class name.
*
* If the descriptor does not define a package name, this method does nothing and returns <code>className</code> unchanged.
*
* If <code>className</code> contains at least one "." character, it is assumed to be already fully qualified and so it
* will be returned unchanged.
*
* If <code>className</code> has no "." characters, and the descriptor defines a package name, that package name
* will prefix the given class name and will form the fully qualified class name that is returned.
*
* @param descriptor a plugin descriptor that may or may not define a package name
* @param className a classname that may or may not be fully qualified
* @return the fully qualified class name or <code>null</code> if <code>className</code> is <code>null</code>
*/
public static String getFullyQualifiedClassName(ServerPluginDescriptorType descriptor, String className) {
if (className != null) {
String pkg = (descriptor != null) ? descriptor.getPackage() : null;
if ((className.indexOf('.') == -1) && (pkg != null)) {
className = pkg + '.' + className;
}
}
return className;
}
/**
* Returns the list of all defined control operations. If there are no controls, an empty list is returned.
*
* @param descriptor
*
* @return list of control operations defined in the descriptor
*
* @throws Exception if the plugin descriptor was invalid
*/
public static List<ControlDefinition> getControlDefinitions(ServerPluginDescriptorType descriptor) throws Exception {
List<ControlDefinition> defs = new ArrayList<ControlDefinition>();
if (descriptor.getPluginComponent() != null) {
List<ControlType> descriptorDefs = descriptor.getPluginComponent().getControl();
if (descriptorDefs != null && descriptorDefs.size() > 0) {
for (ControlType descriptorDef : descriptorDefs) {
String name = descriptorDef.getName();
String displayName = descriptorDef.getDisplayName();
String description = descriptorDef.getDescription();
ConfigurationDefinition params = null;
ConfigurationDefinition results = null;
ConfigurationDescriptor xml = descriptorDef.getParameters();
if (xml != null) {
params = ConfigurationMetadataParser.parse(descriptorDef.getName() + "_params", xml);
}
xml = descriptorDef.getResults();
if (xml != null) {
results = ConfigurationMetadataParser.parse(descriptorDef.getName() + "_results", xml);
}
ControlDefinition def = new ControlDefinition(name, displayName, description, params, results);
defs.add(def);
}
}
}
return defs;
}
/**
* Returns the global configuration definition for the plugin. This does not include any scheduled
* job information - see {@link #getScheduledJobs(ServerPluginDescriptorType)} for that.
*
* @param descriptor
*
* @return the plugin configuration definition, or <code>null</code> if the descriptor did not define plugin config.
*
* @throws Exception if the plugin descriptor was invalid
*/
public static ConfigurationDefinition getPluginConfigurationDefinition(ServerPluginDescriptorType descriptor)
throws Exception {
ConfigurationDefinition config = null;
ConfigurationDescriptor configXml = descriptor.getPluginConfiguration();
if (configXml != null) {
config = ConfigurationMetadataParser.parse(descriptor.getName(), configXml);
}
return config;
}
/**
* Returns the scheduled jobs configuration definition for the plugin.
* Use {@link #getScheduledJobs(ServerPluginDescriptorType)} to return a list of a more
* strongly typed jobs object, as opposed to a generic configuration definition.
*
* @param descriptor
*
* @return the scheduled jobs configuration definition, or <code>null</code> if the descriptor did not define plugin config.
*
* @throws Exception if the plugin descriptor was invalid
*/
public static ConfigurationDefinition getScheduledJobsDefinition(ServerPluginDescriptorType descriptor)
throws Exception {
ConfigurationDefinition config = null;
ConfigurationDescriptor configXml = descriptor.getScheduledJobs();
if (configXml != null) {
config = ConfigurationMetadataParser.parse(descriptor.getName(), configXml);
}
return config;
}
/**
* Given a descriptor, this will parse it and return any scheduled jobs that it finds.
* This essentially gives you a list of the pre-defined jobs, prior to a user customizing
* them with their own settings.
*
* @param descriptor
*
* @return list of jobs that are defined, will be empty if no jobs are defined.
*
* @throws Exception if the plugin descriptor was invalid
*/
public static List<ScheduledJobDefinition> getScheduledJobs(ServerPluginDescriptorType descriptor) throws Exception {
List<ScheduledJobDefinition> jobs = new ArrayList<ScheduledJobDefinition>();
ConfigurationDescriptor configXml = descriptor.getScheduledJobs();
if (configXml != null) {
ConfigurationDefinition config = ConfigurationMetadataParser.parse(descriptor.getName(), configXml);
for (PropertyDefinition propDef : config.getPropertyDefinitions().values()) {
ScheduledJobDefinition jobDef = getScheduledJob(propDef);
if (jobDef != null) {
jobs.add(jobDef);
}
}
}
return jobs;
}
private static ScheduledJobDefinition getScheduledJob(PropertyDefinition mapDef) throws Exception {
ScheduledJobDefinition job = null;
// if the definition is not a map, it can't be a schedule
if (mapDef instanceof PropertyDefinitionMap) {
PropertyDefinitionMap jobMapDef = (PropertyDefinitionMap) mapDef;
ConfigurationTemplate defaultTemplate = jobMapDef.getConfigurationDefinition().getDefaultTemplate();
PropertyMap defaults = defaultTemplate.getConfiguration().getMap(mapDef.getName());
// prepare some defaults if the schedule didn't define some of these
// we assume:
// the map name is the methodName that will be invoked
// the class name is null, which means its the stateful plugin component to be invoked
// the schedule is always enabled
// the schedule is never concurrent
// the schedule is a periodic schedule that triggers every 10 minutes
// the schedule has no callback data
String methodName = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_METHOD_NAME, mapDef.getName());
String className = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CLASS, null);
String enabledStr = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_ENABLED, "true");
String concurrentStr = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CONCURRENT, "false");
String clusteredStr = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CLUSTERED, "true");
String scheduleTypeStr = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_SCHEDULE_TYPE,
PeriodicScheduleType.TYPE_NAME);
String scheduleTriggerStr = defaults.getSimpleValue(SCHEDULED_JOB_PROP_NAME_SCHEDULE_TRIGGER, "600000");
String jobId = jobMapDef.getName();
boolean enabled = Boolean.parseBoolean(enabledStr);
boolean concurrent = Boolean.parseBoolean(concurrentStr);
boolean clustered = Boolean.parseBoolean(clusteredStr);
AbstractScheduleType scheduleType;
scheduleType = AbstractScheduleType.create(concurrent, clustered, scheduleTypeStr, scheduleTriggerStr);
if (scheduleType == null) {
throw new InvalidPluginDescriptorException("Invalid schedule type: " + scheduleTypeStr);
}
// the callback data will contain all simple properties in the schedule job map
Properties callbackData = new Properties();
Map<String, PropertyDefinition> allPropDefs = jobMapDef.getMap();
for (PropertyDefinition currentPropDef : allPropDefs.values()) {
if (currentPropDef instanceof PropertyDefinitionSimple) {
String currentPropDefName = currentPropDef.getName();
String currentPropDefValue = defaults.getSimpleValue(currentPropDefName, null);
if (currentPropDefValue != null) {
callbackData.setProperty(currentPropDefName, currentPropDefValue);
}
}
}
job = new ScheduledJobDefinition(jobId, enabled, className, methodName, scheduleType, callbackData);
} else if (!(mapDef instanceof PropertyDefinitionList)) {
// mapDef isn't even a list (which would have indicated its a valid list-of-maps of jobs) - so assume its invalid
throw new Exception("Invalid scheduled job definition [" + mapDef.getName() + "]");
}
return job;
}
/**
* Given a configuration, this will return any scheduled jobs that are defined in it.
* This essentially gives you a list of the jobs that may be different from the descriptor's
* pre-defined jobs due to a user customizing them with their own settings.
*
* @param jobsConfig the configuration with the scheduled job information
*
* @return list of jobs that are defined, will be empty if no jobs are defined.
*
* @throws Exception if the configuration was invalid
*/
public static List<ScheduledJobDefinition> getScheduledJobs(Configuration scheduledJobsConfig) throws Exception {
List<ScheduledJobDefinition> jobs = new ArrayList<ScheduledJobDefinition>();
if (scheduledJobsConfig != null) {
for (Property prop : scheduledJobsConfig.getProperties()) {
ScheduledJobDefinition jobDef = getScheduledJob(prop);
if (jobDef != null) {
jobs.add(jobDef);
} else {
// this might be a list-o-maps containing a list of user-define jobs
if (prop instanceof PropertyList) {
PropertyList listOfJobs = (PropertyList) prop;
for (Property listItem : listOfJobs.getList()) {
jobDef = getScheduledJob(listItem);
if (jobDef != null) {
jobs.add(jobDef);
}
}
}
}
}
}
return jobs;
}
private static ScheduledJobDefinition getScheduledJob(Property map) throws Exception {
ScheduledJobDefinition job = null;
// if the definition is not a map, it can't be a schedule
if (map instanceof PropertyMap) {
PropertyMap jobMap = (PropertyMap) map;
// prepare some defaults if the schedule didn't define some of these
// we assume:
// the map name is the methodName that will be invoked
// the class name is null, which means its the stateful plugin component to be invoked
// the schedule is always enabled
// the schedule is never concurrent
// the schedule is a periodic schedule that triggers every 10 minutes
// the schedule has no callback data
String methodName = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_METHOD_NAME, jobMap.getName());
String className = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CLASS, null);
String enabledStr = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_ENABLED, "true");
String concurrentStr = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CONCURRENT, "false");
String clusteredStr = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_CLUSTERED, "true");
String scheduleTypeStr = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_SCHEDULE_TYPE,
PeriodicScheduleType.TYPE_NAME);
String scheduleTriggerStr = jobMap.getSimpleValue(SCHEDULED_JOB_PROP_NAME_SCHEDULE_TRIGGER, "600000");
String jobId = jobMap.getName();
boolean enabled = Boolean.parseBoolean(enabledStr);
boolean concurrent = Boolean.parseBoolean(concurrentStr);
boolean clustered = Boolean.parseBoolean(clusteredStr);
AbstractScheduleType scheduleType;
scheduleType = AbstractScheduleType.create(concurrent, clustered, scheduleTypeStr, scheduleTriggerStr);
if (scheduleType == null) {
throw new InvalidPluginDescriptorException("Invalid schedule type: " + scheduleTypeStr);
}
// the callback data will contain all simple properties in the schedule job map
Properties callbackData = new Properties();
Map<String, Property> allProps = jobMap.getMap();
for (Property currentProp : allProps.values()) {
if (currentProp instanceof PropertySimple) {
String currentPropName = currentProp.getName();
String currentPropValue = jobMap.getSimpleValue(currentPropName, null);
if (currentPropValue != null) {
callbackData.setProperty(currentPropName, currentPropValue);
}
}
}
job = new ScheduledJobDefinition(jobId, enabled, className, methodName, scheduleType, callbackData);
}
return job;
}
}