/*
* RHQ Management Platform
* Copyright (C) 2005-2014 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.modules.plugins.wildfly10;
import static org.rhq.modules.plugins.wildfly10.ASConnection.verbose;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
import org.rhq.modules.plugins.wildfly10.json.Address;
import org.rhq.modules.plugins.wildfly10.json.ReadChildrenNames;
import org.rhq.modules.plugins.wildfly10.json.ReadResource;
import org.rhq.modules.plugins.wildfly10.json.Result;
/**
* Discover subsystems. We need to distinguish two cases denoted by the path
* plugin config:
* <ul>
* <li>Path is a single 'word': here the value denotes a key in the resource path
* of AS7, that identifies a child type see e.g. the Connectors below the JBossWeb
* service in the plugin descriptor. There can be multiple resources of the given
* type. In addition it is possible that a path entry in configuration shares multiple
* types that are separated by the pipe symbol.</li>
* <li>Path is a key-value pair (e.g. subsystem=web ). This denotes a singleton
* subsystem with a fixes path within AS7 (perhaps below another resource in the
* tree.</li>
* </ul>
*
* @author Heiko W. Rupp
*/
public class SubsystemDiscovery implements ResourceDiscoveryComponent<BaseComponent<?>> {
private static final Log LOG = LogFactory.getLog(SubsystemDiscovery.class);
private static final Pattern CONF_PATH_PATTERN = Pattern.compile("\\|");
@Override
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<BaseComponent<?>> context)
throws Exception {
Set<DiscoveredResourceDetails> details = new HashSet<DiscoveredResourceDetails>();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationConfig.Feature.READ_ENUMS_USING_TO_STRING, true);
BaseComponent parentComponent = context.getParentResourceComponent();
ASConnection connection = parentComponent.getASConnection();
Configuration config = context.getDefaultPluginConfiguration();
String confPath = config.getSimpleValue("path", "");
if (confPath == null || confPath.isEmpty()) {
LOG.error("Path plugin config is null for ResourceType [" + context.getResourceType().getName() + "].");
return details;
}
// check for "/" which denotes lookup for child resources deeper in DMR tree
// default path can look like "subsystem=a,sub-sub-system=b/resource-type1|resource-type2"
String additionalParentPath = null;
int additionalParentIndex = confPath.indexOf("/");
if (additionalParentIndex > 0) {
additionalParentPath = confPath.substring(0, additionalParentIndex);
confPath = confPath.substring(additionalParentIndex + 1);
}
boolean lookForChildren = false;
if (!confPath.contains("=")) { // NO = -> no sub path, but a type
lookForChildren = true;
}
// check if the parent is a JDG/Infinispan server. In this case ignore the as7 version
// of the type and vice versa
if (shouldSkipEntryWrtIspn(context, confPath)) {
return details;
}
// Construct the full path including the parent
String path;
String parentPath = parentComponent.getPath();
if (parentPath == null || parentPath.isEmpty()) {
parentPath = "";
}
if (additionalParentPath != null) {
parentPath += "," + additionalParentPath;
}
path = parentPath;
PropertySimple managedRuntimeProp = config.getSimple("managedRuntime");
if (managedRuntimeProp != null && managedRuntimeProp.getBooleanValue() != null
&& managedRuntimeProp.getBooleanValue()) {
// path correction for managed servers, where the config is below host=x,server-config=y but
// the runtime resource is below host=x,server=y
if (path.startsWith("host=")) {
path = path.replaceAll(",server-config=", ",server=");
parentPath = parentPath.replaceAll(",server-config=", ",server=");
}
}
if (verbose) {
LOG.info("total path: [" + path + "]");
}
if (lookForChildren) {
// Looking for multiple resource of type 'childType'
// check if there are multiple types are present
List<String> subTypes = new ArrayList<String>();
if (confPath.contains("|")) {
subTypes.addAll(Arrays.asList(CONF_PATH_PATTERN.split(confPath)));
} else
subTypes.add(confPath);
for (String cpath : subTypes) {
Address addr = new Address(parentPath);
Result result = connection.execute(new ReadChildrenNames(addr, cpath));
if (result.isSuccess()) {
@SuppressWarnings("unchecked")
List<String> subsystems = (List<String>) result.getResult();
// There may be multiple children of the given type
for (String val : subsystems) {
String newPath = cpath + "=" + val;
Configuration config2 = context.getDefaultPluginConfiguration();
String resKey;
if (path == null || path.isEmpty())
resKey = newPath;
else {
if (path.startsWith(","))
path = path.substring(1);
resKey = path + "," + cpath + "=" + val;
}
PropertySimple pathProp = new PropertySimple("path", resKey);
config2.put(pathProp);
DiscoveredResourceDetails detail = new DiscoveredResourceDetails(context.getResourceType(), // DataType
resKey, // Key
val, // Name
null, // Version
context.getResourceType().getDescription(), // subsystem.description
config2, null);
details.add(detail);
}
}
}
} else {
// Single subsystem
path += "," + confPath;
if (path.startsWith(","))
path = path.substring(1);
Result result = connection.execute(new ReadResource(new Address(path)));
if (result.isSuccess()) {
String resKey = path;
String name = resKey.substring(resKey.lastIndexOf("=") + 1);
Configuration config2 = context.getDefaultPluginConfiguration();
PropertySimple pathProp = new PropertySimple("path", path);
config2.put(pathProp);
DiscoveredResourceDetails detail = new DiscoveredResourceDetails(context.getResourceType(), // DataType
path, // Key
name, // Name
null, // Version
context.getResourceType().getDescription(), // Description
config2, null);
details.add(detail);
}
}
return details;
}
/**
* The as7 plugin and the JDG/Infinispan Server plugin both have a subsystem=infinispan. We need to decide
* which one to 'activate' depending on the type, plugin and the detected parent.
* Rules are:<ul>
* <li>If the parent is a host controller or such, there is no jdg available</li>
* <li>If parent is eap/as7, use the type from the as7 plugin</li>
* <li>If parent is a jdg server, use the type from the jdg plugin.</li>
* </ul>
*
*
* @param context The parent's resource component
* @param confPath The subsystem that got fed into discovery. Directly return is not subsystem=infinispan
* @return True if this subsystem should be skipped.
*/
protected boolean shouldSkipEntryWrtIspn(ResourceDiscoveryContext<BaseComponent<?>> context, String confPath) {
// If this is not subsystem=infinispan, we should not skip it at all
if (!"subsystem=infinispan".equals(confPath))
return false;
ResourceType ourType = context.getResourceType();
boolean ourPluginTypeIsJdg = ourType.getPlugin().equals("JDG")
|| (ourType.getPlugin().equals("InfinispanServer"));
String productType = context.getParentResourceComponent().pluginConfiguration.getSimpleValue("productType",
"AS7");
boolean isJdgProduct = "JDG".equals(productType) || "ISPN".equals(productType);
if (ourPluginTypeIsJdg && isJdgProduct) {
LOG.debug("Ours is JDG and product is JDG/InfinispanServer");
return false;
}
if (!ourPluginTypeIsJdg && !isJdgProduct) {
if (LOG.isDebugEnabled()) {
LOG.debug("Ours is not JDG (" + ourType.toString() + ") and product is not JDG/InfinispanServer ("
+ productType + ")");
}
return false;
}
return true;
}
}