/* * RHQ Management Platform * Copyright (C) 2005-2012 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.core.tool.plugindoc; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.clientapi.agent.metadata.ConfigurationMetadataParser; import org.rhq.core.clientapi.agent.metadata.ContentMetadataParser; import org.rhq.core.clientapi.agent.metadata.InvalidPluginDescriptorException; import org.rhq.core.clientapi.agent.metadata.MetricsMetadataParser; import org.rhq.core.clientapi.agent.metadata.OperationsMetadataParser; import org.rhq.core.clientapi.descriptor.plugin.ContentDescriptor; import org.rhq.core.clientapi.descriptor.plugin.Help; import org.rhq.core.clientapi.descriptor.plugin.MetricDescriptor; import org.rhq.core.clientapi.descriptor.plugin.OperationDescriptor; import org.rhq.core.clientapi.descriptor.plugin.ParentResourceType; import org.rhq.core.clientapi.descriptor.plugin.PlatformDescriptor; import org.rhq.core.clientapi.descriptor.plugin.PluginDescriptor; import org.rhq.core.clientapi.descriptor.plugin.ProcessScanDescriptor; import org.rhq.core.clientapi.descriptor.plugin.ResourceCreateDeletePolicy; import org.rhq.core.clientapi.descriptor.plugin.ResourceCreationData; import org.rhq.core.clientapi.descriptor.plugin.ResourceDescriptor; import org.rhq.core.clientapi.descriptor.plugin.RunsInsideType; import org.rhq.core.clientapi.descriptor.plugin.ServerDescriptor; import org.rhq.core.clientapi.descriptor.plugin.ServiceDescriptor; import org.rhq.core.domain.measurement.MeasurementDefinition; import org.rhq.core.domain.plugin.Plugin; import org.rhq.core.domain.resource.CreateDeletePolicy; import org.rhq.core.domain.resource.ProcessScan; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceCreationDataType; import org.rhq.core.domain.resource.ResourceType; /** * @author Ian Springer */ public class PluginDescriptorProcessor { private final Log log = LogFactory.getLog(PluginDescriptorProcessor.class); private PluginDescriptor pluginDescriptor; private Plugin plugin; private Map<ResourceType, ResourceType> allTypes = new LinkedHashMap<ResourceType, ResourceType>(); private Set<ResourceType> resourceTypes = new LinkedHashSet<ResourceType>(); public PluginDescriptorProcessor(PluginDescriptor pluginDescriptor) { this.pluginDescriptor = pluginDescriptor; this.plugin = createPlugin(); } public Set<ResourceType> processPluginDescriptor() throws InvalidPluginDescriptorException { for (PlatformDescriptor serverDescriptor : pluginDescriptor.getPlatforms()) { resourceTypes.add(parsePlatformDescriptor(serverDescriptor)); } for (ServerDescriptor serverDescriptor : pluginDescriptor.getServers()) { ResourceType serverType = parseServerDescriptor(serverDescriptor, null); if (isRootType(serverType)) { resourceTypes.add(serverType); } } for (ServiceDescriptor serviceDescriptor : pluginDescriptor.getServices()) { ResourceType serviceType = parseServiceDescriptor(serviceDescriptor, null, null); if (isRootType(serviceType)) { resourceTypes.add(serviceType); } } return this.resourceTypes; } private static boolean isRootType(ResourceType type) { boolean result; if ((type.getParentResourceTypes() == null) || (type.getParentResourceTypes().isEmpty())) { result = true; } else { result = false; for (ResourceType parentType : type.getParentResourceTypes()) { if (!parentType.getPlugin().equals(type.getPlugin())) { result = true; break; } } } return result; } public PluginDescriptor getPluginDescriptor() { return this.pluginDescriptor; } public Plugin getPlugin() { return this.plugin; } private Plugin createPlugin() { Plugin plugin = new Plugin(this.pluginDescriptor.getName(), null); plugin.setDisplayName(this.pluginDescriptor.getDisplayName()); plugin.setDescription(this.pluginDescriptor.getDescription()); plugin.setVersion(this.pluginDescriptor.getVersion()); Help help = this.pluginDescriptor.getHelp(); if ((help != null) && !help.getContent().isEmpty()) { plugin.setHelpContentType(help.getContentType()); plugin.setHelp(String.valueOf(help.getContent().get(0))); } return plugin; } private ResourceType parsePlatformDescriptor(PlatformDescriptor platformDescriptor) throws InvalidPluginDescriptorException { ResourceType platformResourceType = new ResourceType(platformDescriptor.getName(), pluginDescriptor.getName(), ResourceCategory.PLATFORM, null); platformResourceType.setSubCategory(platformDescriptor.getSubCategory()); platformResourceType.setDescription(platformDescriptor.getDescription()); log.debug("Parsed platform resource type: " + platformResourceType); parseResourceDescriptor(platformDescriptor, platformResourceType, null, null, null); if ((platformResourceType.getProcessScans() != null) && (platformResourceType.getProcessScans().size() > 0)) { log.warn("Platforms are not auto-discovered via process scans. " + "The <process-scan> elements will be ignored in resource type: " + platformResourceType); } return platformResourceType; } private ResourceType parseServerDescriptor(ServerDescriptor serverDescriptor, ResourceType parentType) throws InvalidPluginDescriptorException { ResourceType serverResourceType; String sourcePlugin = serverDescriptor.getSourcePlugin(); String sourceServer = serverDescriptor.getSourceType(); sourcePlugin = (sourcePlugin == null) ? "" : sourcePlugin.trim(); sourceServer = (sourceServer == null) ? "" : sourceServer.trim(); if (sourcePlugin.isEmpty() && sourceServer.isEmpty()) { // not using Embedded extension model serverResourceType = new ResourceType(serverDescriptor.getName(), pluginDescriptor.getName(), ResourceCategory.SERVER, parentType); serverResourceType.setSubCategory(serverDescriptor.getSubCategory()); serverResourceType.setDescription(serverDescriptor.getDescription()); serverResourceType.setCreationDataType(convertCreationDataType(serverDescriptor.getCreationDataType())); serverResourceType .setCreateDeletePolicy(convertCreateDeletePolicy(serverDescriptor.getCreateDeletePolicy())); serverResourceType.setSingleton(serverDescriptor.isSingleton()); log.debug("Parsed server resource type: " + serverResourceType); parseResourceDescriptor(serverDescriptor, serverResourceType, null, null, null); } else if ((sourcePlugin.length() > 0) && (sourceServer.length() > 0)) { // using Embedded extension model - the defined type is actually a copy of another plugin's server type log.debug("Parsing embedded server type {" + pluginDescriptor.getName() + "}" + serverDescriptor.getName() + ", which extends server type {" + sourcePlugin + "}" + sourceServer + "..."); Map<String, ServerDescriptor> pluginServerDescriptors = getPluginServerDescriptors(sourcePlugin); ServerDescriptor sourceServerDescriptor = pluginServerDescriptors.get(sourceServer); if (sourceServerDescriptor == null) { log.warn("There is no server type named [" + sourceServer + "] from a plugin named [" + sourcePlugin + "]. This is probably because that plugin is missing. Resource Type [{" + pluginDescriptor.getName() + "}" + serverDescriptor.getName() + "] will be ignored."); return null; } serverResourceType = new ResourceType(serverDescriptor.getName(), pluginDescriptor.getName(), ResourceCategory.SERVER, parentType); // Let the plugin writer override these, or if not, parseResourceDescriptor() will pick up the source type's // values. serverResourceType.setDescription(serverDescriptor.getDescription()); serverResourceType.setSubCategory(serverDescriptor.getSubCategory()); serverResourceType.setCreationDataType(convertCreationDataType(serverDescriptor.getCreationDataType())); serverResourceType .setCreateDeletePolicy(convertCreateDeletePolicy(serverDescriptor.getCreateDeletePolicy())); serverResourceType.setSingleton(serverDescriptor.isSingleton()); parseResourceDescriptor(sourceServerDescriptor, serverResourceType, null, null, sourcePlugin); // The above incorporates children from the source descriptor. The following incorporates // children from this descriptor // Look for child server types for (ServerDescriptor childServerDescriptor : serverDescriptor.getServers()) { parseServerDescriptor(childServerDescriptor, serverResourceType); } // Look for child service types for (ServiceDescriptor childServiceDescriptor : serverDescriptor.getServices()) { parseServiceDescriptor(childServiceDescriptor, serverResourceType, null); } } else { // this should never happen - the XML parser should have failed to even get this far throw new InvalidPluginDescriptorException("Both sourcePlugin and sourceType must be defined: " + serverDescriptor.getName()); } // now see if we are using the Injection extension model // if so, we need to inject the new resource type as a child to the parent plugin's types if (parentType == null) { addRunsInsideParentTypes(serverDescriptor, serverResourceType); } // Look for child server types for (ServerDescriptor childServerDescriptor : serverDescriptor.getServers()) { parseServerDescriptor(childServerDescriptor, serverResourceType); } return serverResourceType; } private ResourceType parseServiceDescriptor(ServiceDescriptor serviceDescriptor, ResourceType parentType, String parentSourcePlugin) throws InvalidPluginDescriptorException { ResourceType serviceResourceType; String sourcePlugin = serviceDescriptor.getSourcePlugin(); // Fallback to using the source plugin of your parent if you don't override. if (sourcePlugin == null) { sourcePlugin = parentSourcePlugin; } String sourceType = serviceDescriptor.getSourceType(); sourcePlugin = (sourcePlugin == null) ? "" : sourcePlugin.trim(); sourceType = (sourceType == null) ? "" : sourceType.trim(); if (sourcePlugin.isEmpty() && sourceType.isEmpty()) { // not using Embedded extension model serviceResourceType = new ResourceType(serviceDescriptor.getName(), pluginDescriptor.getName(), ResourceCategory.SERVICE, parentType); serviceResourceType.setSubCategory(serviceDescriptor.getSubCategory()); serviceResourceType.setDescription(serviceDescriptor.getDescription()); serviceResourceType.setCreationDataType(convertCreationDataType(serviceDescriptor.getCreationDataType())); serviceResourceType.setCreateDeletePolicy(convertCreateDeletePolicy(serviceDescriptor .getCreateDeletePolicy())); serviceResourceType.setSingleton(serviceDescriptor.isSingleton()); log.debug("Parsed service resource type: " + serviceResourceType); parseResourceDescriptor(serviceDescriptor, serviceResourceType, null, null, null); if ((serviceResourceType.getProcessScans() != null) && (serviceResourceType.getProcessScans().size() > 0)) { log.warn("Child services are not auto-discovered via process scans. " + "The <process-scan> elements will be ignored in resource type: " + serviceResourceType); } } else if (sourcePlugin.length() > 0) { // Using Embedded extension model - the defined type is actually a copy of another plugin's service or server type. log.debug("Parsing embedded service type {" + pluginDescriptor.getName() + "}" + serviceDescriptor.getName() + ", which extends type {" + sourcePlugin + "}" + sourceType + "..."); ResourceDescriptor sourceTypeDescriptor; if (sourceType.isEmpty()) { sourceTypeDescriptor = serviceDescriptor; } else { Map<String, ServiceDescriptor> pluginServiceDescriptors = getPluginServiceDescriptors(sourcePlugin); sourceTypeDescriptor = pluginServiceDescriptors.get(sourceType); if (sourceTypeDescriptor == null) { Map<String, ServerDescriptor> pluginServerDescriptors = getPluginServerDescriptors(sourcePlugin); sourceTypeDescriptor = pluginServerDescriptors.get(sourceType); } } if (sourceTypeDescriptor == null) { log.warn("There is no service or server type named [" + sourceType + "] from a plugin named [" + sourcePlugin + "]. This is probably because that plugin is missing. Resource Type [{" + pluginDescriptor.getName() + "}" + serviceDescriptor.getName() + "] will be ignored."); return null; } serviceResourceType = new ResourceType(serviceDescriptor.getName(), pluginDescriptor.getName(), ResourceCategory.SERVICE, parentType); // Let the plugin writer override these, or if not, parseResourceDescriptor() will pick up the source type's // values. serviceResourceType.setDescription(serviceDescriptor.getDescription()); serviceResourceType.setSubCategory(serviceDescriptor.getSubCategory()); serviceResourceType.setCreationDataType(convertCreationDataType(serviceDescriptor.getCreationDataType())); serviceResourceType.setCreateDeletePolicy(convertCreateDeletePolicy(serviceDescriptor .getCreateDeletePolicy())); serviceResourceType.setSingleton(serviceDescriptor.isSingleton()); parseResourceDescriptor(sourceTypeDescriptor, serviceResourceType, null, null, sourcePlugin); } else { // this should never happen - the XML parser should have failed to even get this far throw new InvalidPluginDescriptorException("Both sourcePlugin and sourceType must be defined: " + serviceDescriptor.getName()); } // now see if we are using the Injection extension model // if so, we need to inject the new resource type as a child to the parent plugin's types // note that the Injection model only allows for root-level services to be injected if (parentType == null) { addRunsInsideParentTypes(serviceDescriptor, serviceResourceType); } return serviceResourceType; } private void addRunsInsideParentTypes(ResourceDescriptor resourceDescriptor, ResourceType resourceType) { RunsInsideType runsInside = resourceDescriptor.getRunsInside(); if (runsInside != null) { List<ParentResourceType> parentTypesDescriptor = runsInside.getParentResourceType(); for (ParentResourceType parentTypeDescriptor : parentTypesDescriptor) { ResourceCategory parentResourceCategory = parentTypeDescriptor.getPlugin().equals("Platforms") ? ResourceCategory.PLATFORM : ResourceCategory.SERVER; ResourceType parentResourceType = new ResourceType(parentTypeDescriptor.getName(), parentTypeDescriptor.getPlugin(), parentResourceCategory, ResourceType.ANY_PLATFORM_TYPE); ResourceType fullParentType = allTypes.get(parentResourceType); if (fullParentType != null) { parentResourceType = fullParentType; } resourceType.addParentResourceType(parentResourceType); } } } /** * Parses the resource descriptor and registers the type and its component classes. * * @param resourceDescriptor * @param resourceType the new resource type that will get the new data found in the descriptor added to it * @param discoveryClass if <code>null</code>, will get the classname from the resourceDescriptor; otherwise, * is used as-is * @param componentClass if <code>null</code>, will get the classname from the resourceDescriptor; otherwise, * is used as-is * * @throws InvalidPluginDescriptorException */ private void parseResourceDescriptor(ResourceDescriptor resourceDescriptor, ResourceType resourceType, String discoveryClass, String componentClass, String sourcePlugin) throws InvalidPluginDescriptorException { // 0) classes // 1) Plugin config // 2) Resource config // 3) Metrics // 4) Control operations // 5) Process matches (for process scan auto-discovery) // 6) Artifacts // 7) Sub categories resourceType.setPlugin(pluginDescriptor.getName()); if (resourceDescriptor.getPluginConfiguration() != null) { resourceType.setPluginConfigurationDefinition(ConfigurationMetadataParser.parse(resourceType.getName(), resourceDescriptor.getPluginConfiguration())); } if (resourceDescriptor.getResourceConfiguration() != null) { resourceType.setResourceConfigurationDefinition(ConfigurationMetadataParser.parse(resourceType .getName(), resourceDescriptor.getResourceConfiguration())); } int displayPosition = 1; for (MetricDescriptor metricDescriptor : resourceDescriptor.getMetric()) { List<MeasurementDefinition> measurementDefinitions = MetricsMetadataParser.parseMetricsMetadata( metricDescriptor, resourceType); for (MeasurementDefinition measurementDefinition : measurementDefinitions) { measurementDefinition.setDisplayOrder(displayPosition++); resourceType.addMetricDefinition(measurementDefinition); } } for (OperationDescriptor operationDescriptor : resourceDescriptor.getOperation()) { resourceType.addOperationDefinition(OperationsMetadataParser .parseOperationDescriptor(operationDescriptor)); } for (ProcessScanDescriptor processMatch : resourceDescriptor.getProcessScan()) { System.out.println(resourceType.getName() + ": " + new ProcessScan(processMatch.getQuery(), processMatch.getName())); resourceType.addProcessScan(new ProcessScan(processMatch.getQuery(), processMatch.getName())); } for (ContentDescriptor contentDescriptor : resourceDescriptor.getContent()) { resourceType.addPackageType(ContentMetadataParser.parseContentDescriptor(contentDescriptor)); } Help help = resourceDescriptor.getHelp(); if ((help != null) && !help.getContent().isEmpty()) { resourceType.setHelpTextContentType(help.getContentType()); resourceType.setHelpText(String.valueOf(help.getContent().get(0))); } allTypes.put(resourceType, resourceType); // The type is built, register it //registerResourceTypeAndComponentClasses(resourceType, discoveryClass, componentClass); // Look for child types if (resourceDescriptor instanceof PlatformDescriptor) { for (ServerDescriptor serverDescriptor : ((PlatformDescriptor) resourceDescriptor).getServers()) { parseServerDescriptor(serverDescriptor, resourceType); } for (ServiceDescriptor serviceDescriptor : ((PlatformDescriptor) resourceDescriptor).getServices()) { parseServiceDescriptor(serviceDescriptor, resourceType, sourcePlugin); } } if (resourceDescriptor instanceof ServerDescriptor) { for (ServerDescriptor serverDescriptor : ((ServerDescriptor) resourceDescriptor).getServers()) { parseServerDescriptor(serverDescriptor, resourceType); } for (ServiceDescriptor serviceDescriptor : ((ServerDescriptor) resourceDescriptor).getServices()) { parseServiceDescriptor(serviceDescriptor, resourceType, sourcePlugin); } } if (resourceDescriptor instanceof ServiceDescriptor) { for (ServiceDescriptor serviceDescriptor : ((ServiceDescriptor) resourceDescriptor).getServices()) { parseServiceDescriptor(serviceDescriptor, resourceType, sourcePlugin); } } } private String getFullyQualifiedComponentClassName(String packageName, String baseClassName) { if (baseClassName == null) { return null; } if ((baseClassName.indexOf('.') > -1) || (packageName == null)) { return baseClassName; // looks like must already be fully qualified } return packageName + '.' + baseClassName; } private Map<String, ServerDescriptor> getPluginServerDescriptors(String pluginName) { Map<String, ServerDescriptor> pluginServerDescriptors = new HashMap<String, ServerDescriptor>(); // In plugindoc, we only support embedding types from this plugin. if (pluginName.equals(this.pluginDescriptor.getName())) { for (ServerDescriptor server : pluginDescriptor.getServers()) { pluginServerDescriptors.put(server.getName(), server); } } return pluginServerDescriptors; } private Map<String, ServiceDescriptor> getPluginServiceDescriptors(String pluginName) { Map<String, ServiceDescriptor> pluginServiceDescriptors = new HashMap<String, ServiceDescriptor>(); // In plugindoc, we only support embedding types from this plugin. if (pluginName.equals(this.pluginDescriptor.getName())) { addPluginServiceDescriptors(pluginDescriptor.getServices(), pluginServiceDescriptors); addPluginServiceDescriptors(pluginDescriptor.getServers(), pluginServiceDescriptors); } return pluginServiceDescriptors; } private void addPluginServiceDescriptors(Collection<? extends ResourceDescriptor> parents, Map<String, ServiceDescriptor> descriptors) { if (parents != null) { for (ResourceDescriptor parent : parents) { List<ServiceDescriptor> services; if (parent instanceof ServerDescriptor) { services = ((ServerDescriptor) parent).getServices(); } else if (parent instanceof ServiceDescriptor) { services = ((ServiceDescriptor) parent).getServices(); } else { throw new IllegalStateException("Unsupported parent descriptor type: " + parent.getClass().getName()); } for (ServiceDescriptor service : services) { descriptors.put(service.getName(), service); addPluginServiceDescriptors(service.getServices(), descriptors); // recurse down the hierarchy } } } } /** * Converts the creation data descriptor (JAXB) object into the domain enumeration. * * @param creationType descriptor read creation data object * * @return domain enumeration value; <code>null</code> if the JAXB representation is <code>null</code> or does not * correspond to any domain enumerated value */ private ResourceCreationDataType convertCreationDataType(ResourceCreationData creationType) { switch (creationType) { case CONTENT: { return ResourceCreationDataType.CONTENT; } case CONFIGURATION: { return ResourceCreationDataType.CONFIGURATION; } } return null; } /** * Converts the create and delete policy descriptor (JAXB) object into the domain enumeration. * * @param policy descriptor read policy * * @return domain enumeration value; <code>null</code> if the JAXB representation is <code>null</code> or does not * correspond to any domain enumerated value */ private CreateDeletePolicy convertCreateDeletePolicy(ResourceCreateDeletePolicy policy) { switch (policy) { case BOTH: { return CreateDeletePolicy.BOTH; } case CREATE_ONLY: { return CreateDeletePolicy.CREATE_ONLY; } case DELETE_ONLY: { return CreateDeletePolicy.DELETE_ONLY; } case NEITHER: { return CreateDeletePolicy.NEITHER; } } return null; } }