/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Red Hat licenses this file to you under the Apache License, version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.jbosson.plugins.fuse;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.ClassLoaderFacet;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
import org.rhq.core.pluginapi.util.CommandLineOption;
import org.rhq.core.pluginapi.util.JavaCommandLine;
import org.rhq.core.pluginapi.util.ServerStartScriptDiscoveryUtility;
import org.rhq.core.pluginapi.util.StartScriptConfiguration;
import org.rhq.core.system.ProcessInfo;
import org.rhq.plugins.jmx.JMXDiscoveryComponent;
import org.rhq.plugins.jmx.JMXServerComponent;

/**
 * @author dbokde
 */
public class JBossFuseContainerDiscoveryComponent extends FuseServerDiscoveryComponent implements ClassLoaderFacet {

    private static final Log log = LogFactory.getLog(JBossFuseContainerDiscoveryComponent.class);

    public static final boolean OS_IS_WINDOWS = (File.separatorChar == '\\');
    public static final String KARAF_BASE_PROPERTY = "karaf.base";
    public static final String KARAF_HOME_PROPERTY = "karaf.home";
    public static final String KARAF_NAME_PROPERTY = "karaf.name";
    public static final String ZOOKEEPER_URL_PROPERTY = "zookeeper.url";
    public static final String ZOOKEEPER_PASSWORD_PROPERTY = "zookeeper.password";
    public static final String GIT_REPO_PROPERTY = "git.root";

    private static final String CHILD_PREFIX = "Child ";
    private static final String DEFAULT_KARAF_NAME = "root";

    private static final String ETC_SYSTEM_PROPERTIES = "etc/system.properties";
    private static final String JBOSS_FUSE_PRODUCT = "JBoss Fuse";
    private static final String JBOSS_A_MQ_PRODUCT = "JBoss A-MQ";
    private static final String JBOSS_FUSE_CONTAINER = "JBoss Fuse Container";
    private static final String CONTAINER_TYPE_PROPERTY = "container.type";
    private static final String GIT_REPO_HOME = "data/git";

    // The list of environment vars that the Karaf start script will use if they are set.
    private static final Set<String> START_SCRIPT_ENV_VAR_NAMES = new LinkedHashSet<String>();
    static {
        START_SCRIPT_ENV_VAR_NAMES.addAll(Arrays.asList( //
            "JAVA", //
            "JAVA_DEBUG_OPTS", //
            "JAVA_HOME", //
            "JAVA_MAX_MEM", //
            "JAVA_MAX_PERM_MEM", //
            "JAVA_MIN_MEM", //
            "JAVA_OPTS", //
            "JAVA_PERM_MEM", //
            "KARAF_BASE", //
            "KARAF_DATA", //
            "KARAF_DEBUG", //
            "KARAF_ETC", //
            "KARAF_HOME", //
            "MAX_FD"
        ));

        // If OS is Windows, add env vars that are only used by the batch files.
        if (OS_IS_WINDOWS) {
            START_SCRIPT_ENV_VAR_NAMES.add("ECHO");
            START_SCRIPT_ENV_VAR_NAMES.add("KARAF_TITLE");
            START_SCRIPT_ENV_VAR_NAMES.add("PAUSE");
            START_SCRIPT_ENV_VAR_NAMES.add("SHIFT");
        }
    }

    private static final Set<CommandLineOption> START_SCRIPT_OPTION_EXCLUDES = new HashSet<CommandLineOption>();
    static {
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dcom.sun.management.jmxremote", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Djava.endorsed.dirs", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Djava.ext.dirs", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.instances", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.home", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.base", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.data", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.etc", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Djava.io.tmpdir", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Djava.util.logging.config.file", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.startLocalConsole", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("Dkaraf.startRemoteShell", null));
        START_SCRIPT_OPTION_EXCLUDES.add(new CommandLineOption("classpath", null));
    }

    @Override
    protected boolean populateResourceProperties(ResourceDiscoveryContext context, DiscoveredResourceDetails details) {
        // make sure system properties are set first
        if (!super.populateResourceProperties(context, details)) {
            return false;
        }

        // check if karaf.base matches karaf.home, if it doesn't add CHILD_PREFIX to name and description
        final Configuration pluginConfiguration = details.getPluginConfiguration();
        final String karafHomePath = pluginConfiguration.getSimpleValue(KARAF_HOME_PROPERTY);
        final String karafBasePath = pluginConfiguration.getSimpleValue(KARAF_BASE_PROPERTY);

        final String resourceTypeName = details.getResourceType().getName();
        if (!karafHomePath.equals(karafBasePath)) {
            // replace resource type in name and description to "Child " + resourceType name
            details.setResourceName(details.getResourceName().replace(resourceTypeName, CHILD_PREFIX + resourceTypeName));
            details.setResourceDescription(details.getResourceDescription().replace(resourceTypeName, CHILD_PREFIX + resourceTypeName));
        }

        final File karafHome = new File(karafHomePath);
        final File karafBase = new File(karafBasePath);

        // get container name from system.properties
        final ProcessInfo processInfo = details.getProcessInfo();
        final Properties serverProperties = getServerProperties(karafBase, processInfo);
        String karafName = serverProperties.getProperty(KARAF_NAME_PROPERTY);
        if (log.isDebugEnabled()) {
            log.debug("Found " + details.getResourceType().getName() + " with container name: " + karafName);
        }

        // set Fabric Container name
        pluginConfiguration.setSimpleValue(KARAF_NAME_PROPERTY, karafName);

        // name the product appropriately
        String productName = null;

        // determine version from product specific version file
        String resourceVersion = null;

        // is this a JBoss Fuse install?
        File fuseEsbScript = findVersionFile(karafHome, Pattern.compile("bin/fuse(.bat)?$"));
        if (fuseEsbScript != null) {
            productName = JBOSS_FUSE_PRODUCT;
            // extract product version using product specific versionFile
            pluginConfiguration.setSimpleValue(VERSION_FILE_PROPERTY, "system/org/jboss/fuse/jboss-fuse/(6\\.[0-9\\.\\-(redhat)]+)");
            resourceVersion = getResourceVersion(pluginConfiguration, karafHome);
        }
        // if the esb script or esb version file was not found
        if (fuseEsbScript == null || resourceVersion == null) {
            // is this a JBoss Fuse A-MQ install?
            File fuseMqScript = findVersionFile(karafHome, Pattern.compile("bin/amq(.bat)?$"));
            if (fuseMqScript != null) {
                productName = JBOSS_A_MQ_PRODUCT;
                // extract product version using product specific versionFile
                pluginConfiguration.setSimpleValue(VERSION_FILE_PROPERTY, "system/org/jboss/amq/mq-fabric/(6\\.[0-9\\.\\-(redhat)]+)");
                resourceVersion = getResourceVersion(pluginConfiguration, karafHome);

                if(resourceVersion == null){
                    pluginConfiguration.setSimpleValue(VERSION_FILE_PROPERTY, "system/io/fabric8/mq/mq-fabric/(1\\.[0-9\\.\\-(redhat)]+)");
                    resourceVersion = getResourceVersion(pluginConfiguration, karafHome);
                }
            }
        }

        // product specific script may have been found, but the version file may be missing
        if (resourceVersion == null) {
            // use the default Fabric container version for generic container
            resourceVersion = details.getResourceVersion();
            productName = JBOSS_FUSE_CONTAINER;
        }

        // remember the product name for initLogEventSourcesConfigProp()
        pluginConfiguration.setSimpleValue(CONTAINER_TYPE_PROPERTY, productName);

        // replace generic Fabric server name with product name in resource name, description, and resource key
        final String oldResourceKey = details.getResourceKey();
        details.setResourceKey(oldResourceKey.replace(resourceTypeName,
            productName));
        // replace resource key in name first, so we don't end up adding container name in key prefix
        details.setResourceName(details.getResourceName().replace(oldResourceKey,
            details.getResourceKey()));
        // then replace the trailing product name
        details.setResourceName(details.getResourceName().replace(resourceTypeName,
            productName + " [" + karafName + "]"));
        details.setResourceDescription(details.getResourceDescription().replace(resourceTypeName,
            productName));
        // update to product specific version, if found
        details.setResourceVersion(resourceVersion);

        // find zookeeper url if not set already in getConfigWithJmxServiceUrl()
        final String zkProperty = pluginConfiguration.getSimpleValue(ZOOKEEPER_URL_PROPERTY);
        if (zkProperty == null) {
            final String[] zookeeperUrlPassword = getZookeeperUrlPassword(karafHome, karafBase, processInfo);

            pluginConfiguration.setSimpleValue(ZOOKEEPER_URL_PROPERTY, zookeeperUrlPassword[0]);
            pluginConfiguration.setSimpleValue(ZOOKEEPER_PASSWORD_PROPERTY, zookeeperUrlPassword[1]);
        }

        // find and set Git repo in configuration
        final File gitIndex = findVersionFile(new File(karafBase, GIT_REPO_HOME), Pattern.compile("\\.git/index$"));
        if (gitIndex != null) {
            pluginConfiguration.setSimpleValue(GIT_REPO_PROPERTY, gitIndex.getAbsoluteFile().getParentFile().getParent());
        }

        // set process start script properties if the server is not manually added to inventory
        if (pluginConfiguration.getSimple("manuallyAdded") == null) {
            final JavaCommandLine commandLine = new JavaCommandLine(processInfo.getCommandLine(), true,
                EnumSet.of(JavaCommandLine.OptionValueDelimiter.WHITESPACE,
                    JavaCommandLine.OptionValueDelimiter.EQUALS_SIGN),
                EnumSet.of(JavaCommandLine.OptionValueDelimiter.WHITESPACE,
                    JavaCommandLine.OptionValueDelimiter.EQUALS_SIGN));
            setStartScriptPluginConfigProps(processInfo, commandLine, pluginConfiguration,
                context.getSystemInformation().getThisProcess());
        }

        // FUSEJON-102: for JBoss Fuse 6.2 or Fabric 1.2 RBAC, default JVM connector doesn't work
        // plugin MUST use remote connector by reading jmx serviceUrl from .cfg file
        if (resourceVersion.matches("(^6\\.2\\.[0-9]\\.redhat-[0-9]+)|(^1\\.2\\..*)") &&
            pluginConfiguration.getSimpleValue(JMXServerComponent.PRINCIPAL_CONFIG_PROP) == null) {

            readJMXConfig(pluginConfiguration, karafBasePath, serverProperties);
            details.setResourceDescription(
                details.getResourceDescription().replace("Sun JVM Attach API", "JMX Remoting"));
        }

        return true;
    }

    private Properties getServerProperties(File karafBase, ProcessInfo process) {
        final Properties serverProperties = new Properties();
        serverProperties.setProperty(KARAF_NAME_PROPERTY, DEFAULT_KARAF_NAME);

        // can't use findVersionFile, since it could get an instance/*/etc/system.properties for root
        File systemPropertiesFile = new File(karafBase, ETC_SYSTEM_PROPERTIES);
        loadPropertiesFile(systemPropertiesFile, serverProperties);

        // override system properties with process parameters if present
        if (process != null) {
            final JavaCommandLine commandLine = new JavaCommandLine(process.getCommandLine(), true,
                EnumSet.of(JavaCommandLine.OptionValueDelimiter.WHITESPACE,
                    JavaCommandLine.OptionValueDelimiter.EQUALS_SIGN),
                EnumSet.of(JavaCommandLine.OptionValueDelimiter.WHITESPACE,
                    JavaCommandLine.OptionValueDelimiter.EQUALS_SIGN));
            serverProperties.putAll(commandLine.getSystemProperties());
        }

        return serverProperties;
    }

    private String[] getZookeeperUrlPassword(File karafHome, File karafBase, ProcessInfo process) {

        final Properties serverProperties = getServerProperties(karafBase, process);
        String zookeeperUrl = serverProperties.getProperty(ZOOKEEPER_URL_PROPERTY);
        String zookeeperPassword = serverProperties.getProperty(ZOOKEEPER_PASSWORD_PROPERTY);

        if (zookeeperUrl == null) {

            if (!karafHome.equals(karafBase)) {

                // optimized search in instance.properties
                String[] zookeeperUrlPassword = getChildZooKeeperUrlPassword(karafBase.getAbsolutePath(),
                    serverProperties.getProperty(KARAF_NAME_PROPERTY));
                zookeeperUrl = zookeeperUrlPassword[0];
                zookeeperPassword = zookeeperUrlPassword[1];

            } else {
                // NOTE last resort, search for zookeeper server config in the ensemble root container
                // only works if the ZK server was created before auto-discovery
                log.debug("Looking for " + ZOOKEEPER_URL_PROPERTY + " in data/cache/**/zookeeper.config");
                File zookeperConfig = findVersionFile(new File(karafBase, "data/cache"),
                    Pattern.compile("config/((org/fusesource/fabric)|(io/fabric8))/zookeeper.config"));

                if (zookeperConfig != null) {
                    log.debug("Found zookeeper.config at " + zookeperConfig);
                    Properties zkConfigProps = new Properties();
                    loadPropertiesFile(zookeperConfig, zkConfigProps);
                    zookeeperUrl = zkConfigProps.getProperty(ZOOKEEPER_URL_PROPERTY);
                    zookeeperPassword = zkConfigProps.getProperty(ZOOKEEPER_PASSWORD_PROPERTY);
                }
            }

        } else {

            if (log.isDebugEnabled()) {
                log.debug(String.format("Found %s in file %s under directory %s",
                    ZOOKEEPER_URL_PROPERTY, ETC_SYSTEM_PROPERTIES, karafBase));
                log.debug(String.format("Found %s in file %s under directory %s",
                    ZOOKEEPER_PASSWORD_PROPERTY, ETC_SYSTEM_PROPERTIES, karafBase));
            }
        }

        if (zookeeperUrl != null) {
            // strip quotes
            zookeeperUrl = zookeeperUrl.replaceAll("\"", "");
        }
        if (zookeeperPassword != null) {
            zookeeperPassword = zookeeperPassword.replaceAll("\"", "");
        }

        return new String[] { zookeeperUrl, zookeeperPassword };
    }

    @Override
    protected Configuration getConfigWithJmxServiceUrl(ResourceDiscoveryContext context, ProcessInfo process) {

        try {
            final Configuration pluginConfiguration = context.getDefaultPluginConfiguration();

            // read etc/org.apache.karaf.management.cfg to get serviceUrl property
            final String basePath = getSystemPropertyValue(process, KARAF_BASE_PROPERTY);
            final File karafBase = new File(basePath);
            final Properties serverProps = getServerProperties(karafBase, process);
            readJMXConfig(pluginConfiguration, basePath, serverProps);

            // look for zookeeper.url property for managed containers
            final String[] zookeeperUrlPassword = getZookeeperUrlPassword(
                new File(getSystemPropertyValue(process, KARAF_HOME_PROPERTY)),
                karafBase, process);
            // put this in the configuration to avoid looking it up again
            pluginConfiguration.setSimpleValue(ZOOKEEPER_URL_PROPERTY, zookeeperUrlPassword[0]);
            pluginConfiguration.setSimpleValue(ZOOKEEPER_PASSWORD_PROPERTY, zookeeperUrlPassword[1]);

            if (zookeeperUrlPassword[0] != null) {
                // TODO try getting JMX user name and password from ZK
                // for now, set a default user name and password
                log.warn("Container uses Fabric Zookeeper registry, using default JMX user");
                pluginConfiguration.setSimpleValue(JMXServerComponent.PRINCIPAL_CONFIG_PROP, "admin");
                pluginConfiguration.setSimpleValue(JMXServerComponent.CREDENTIALS_CONFIG_PROP, "admin");
            }

            return pluginConfiguration;

        } catch (Exception e) {
            // avoid stopping auto discovery for JMX property search errors
            log.warn("Error getting JMX properties from Fabric container: [" + e.getMessage() +
                "], using default connection properties", e);
        }

        // if everything else fails, fall back to defaults from rhq-plugin.xml
        return super.getConfigWithJmxServiceUrl(context, process);
    }

    private void readJMXConfig(Configuration pluginConfiguration, String basePath, Properties serverProps) {
        loadPropertiesFile(new File(basePath, "etc/org.apache.karaf.management.cfg"), serverProps);

        String serviceUrl = serverProps.getProperty("serviceUrl");
        if (serviceUrl != null) {
            Pattern propPattern = Pattern.compile("\\$\\{[^\\{\\}]+\\}");
            Matcher matcher = propPattern.matcher(serviceUrl);
            while(matcher.find()) {
                final String property = matcher.group();
                final String value = serverProps.getProperty(property.substring(2, property.length() - 1));
                if (value == null) {
                    throw new InvalidPluginConfigurationException("Missing value for property " +
                        property + " in serviceUrl");
                }
                serviceUrl = serviceUrl.replace(property, value);
            }
            pluginConfiguration.setSimpleValue(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY, serviceUrl);
        } else {
            log.warn("Unable to read JMX URL from etc/org.apache.karaf.management.cfg");
        }

        // get admin role using jmxRole from org.apache.karaf.management.cfg or karaf.admin.role from system.properties
        String adminRole = serverProps.getProperty("jmxRole");
        if (adminRole == null) {
            adminRole = serverProps.getProperty("karaf.admin.role");
            // TODO also handle JAAS principal class in role
            if (adminRole == null) {
                log.debug("Using default admin role [admin]");
                adminRole = "admin";
            }
        }

        // get jmx user name and password from users.properties
        // BIG assumption here that the container is using users.properties!!!,
        // if the instance is configured with another provider, we can't get the user/password anyway
        Properties usersProperties = new Properties();
        loadPropertiesFile(new File(basePath, "etc/users.properties"), usersProperties);
        for (Map.Entry<Object, Object> entry : usersProperties.entrySet()) {
            String value = (String) entry.getValue();
            if (value.contains(adminRole)) {
                pluginConfiguration.setSimpleValue(JMXServerComponent.PRINCIPAL_CONFIG_PROP, (String) entry.getKey());
                pluginConfiguration.setSimpleValue(JMXServerComponent.CREDENTIALS_CONFIG_PROP, value.substring(0, value.indexOf(',')));
                break;
            }
        }
    }


    private String[] getChildZooKeeperUrlPassword(String basePath, String karafName) {

        final File karafBase = new File(basePath);
        Properties instanceProps = new Properties();

        final File propertiesFile = new File(karafBase.getParent(), "instance.properties");
        loadPropertiesFile(propertiesFile, instanceProps);

        String zooKeeperUrl = null;
        String zooKeeperPassword = null;
        for (Map.Entry<Object, Object> entry : instanceProps.entrySet()) {
            if (karafName.equals(entry.getValue())) {
                final String key = (String)entry.getKey();
                final String instanceOpts = instanceProps.getProperty(key.replace("name", "opts"));
                // extract ZK url if set
                if (instanceOpts.contains(ZOOKEEPER_URL_PROPERTY)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Found " + ZOOKEEPER_URL_PROPERTY + " in " + propertiesFile);
                    }
                    // strip the leading \="
                    zooKeeperUrl = instanceOpts.substring(
                        instanceOpts.indexOf(ZOOKEEPER_URL_PROPERTY) + ZOOKEEPER_URL_PROPERTY.length() + 2);
                    // strip the trailing "
                    final int end = zooKeeperUrl.indexOf(' ') != -1  ?
                        (zooKeeperUrl.indexOf(' ')  - 1) : zooKeeperUrl.length();
                    zooKeeperUrl = zooKeeperUrl.substring(0, end);
                }
                // extract ZK password if set
                if (instanceOpts.contains(ZOOKEEPER_PASSWORD_PROPERTY)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Found " + ZOOKEEPER_PASSWORD_PROPERTY + " in " + propertiesFile);
                    }
                    // strip the leading \="
                    zooKeeperPassword = instanceOpts.substring(
                        instanceOpts.indexOf(ZOOKEEPER_PASSWORD_PROPERTY) + ZOOKEEPER_PASSWORD_PROPERTY.length() + 2);
                    // strip the trailing "
                    final int end = zooKeeperPassword.indexOf(' ') != -1  ?
                        (zooKeeperPassword.indexOf(' ')  - 1) : zooKeeperPassword.length();
                    zooKeeperPassword = zooKeeperPassword.substring(0, end);
                }
                break;
            }
        }

        return new String[] { zooKeeperUrl, zooKeeperPassword };
    }

    @Override
    protected void initLogEventSourcesConfigProp(File homeDir, Configuration pluginConfiguration, ProcessInfo process) {
        // look for log file based on product type since Enterprise products use product specific log file name
        final String karafHome = pluginConfiguration.getSimpleValue(KARAF_HOME_PROPERTY);
        final String karafBase = pluginConfiguration.getSimpleValue(KARAF_BASE_PROPERTY);
        // default log file for all containers
        String logFile = "{karaf.base}/data/log/karaf.log";
        // all child containers use the same log file name karaf.log
        if (karafHome.equals(karafBase)) {
            final String productType = pluginConfiguration.getSimpleValue(CONTAINER_TYPE_PROPERTY);
            if (JBOSS_FUSE_PRODUCT.equals(productType)) {
                logFile = "{karaf.base}/data/log/fuse.log";
            } else if (JBOSS_A_MQ_PRODUCT.equals(productType)) {
                logFile = "{karaf.base}/data/log/amq.log";
            }
        }
        // update the logFile property to create the appropriate log event configuration
        pluginConfiguration.setSimpleValue(LOG_FILE_PROPERTY, logFile);

        super.initLogEventSourcesConfigProp(homeDir, pluginConfiguration, process);
    }

    private void loadPropertiesFile(File propertiesFile, Properties properties) {

        if (propertiesFile.exists()) {

            FileInputStream stream = null;
            try {
                stream = new FileInputStream(propertiesFile);
                properties.load(stream);
            } catch (IOException e) {
                String message = "Error reading " + propertiesFile;
                log.warn(message, e);
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        // ignore
                    }
                }
            }

        } else {
            log.warn("File " + propertiesFile.getAbsolutePath() + " does not exist");
        }

    }

    // add fabric-linkedin-zookeeper jar from container's system directory
    public List<URL> getAdditionalClasspathUrls(ResourceDiscoveryContext context, DiscoveredResourceDetails details) throws Exception {

        final List<URL> classpath = new ArrayList<URL>();

        final String karafHomePath = details.getPluginConfiguration().getSimpleValue(KARAF_HOME_PROPERTY);
        final File systemDir = new File(karafHomePath, "system");
        File zooKeeperLib = findVersionFile(systemDir, Pattern.compile("fabric-linkedin-zookeeper-.*\\.jar"));

        if (zooKeeperLib == null) {
            log.debug("Missing ZooKeeper library fabric-linkedin-zookeeper*.jar in " + karafHomePath + "/system");

            // look for Apache Curator library in fabric-zookeeper-*.jar
            zooKeeperLib = findVersionFile(systemDir, Pattern.compile("fabric-zookeeper-1.*\\.jar"));
            if (zooKeeperLib == null) {
                throw new InvalidPluginConfigurationException(
                        "Missing ZooKeeper libraries fabric-linkedin-zookeeper-*.jar and fabric-zookeeper-*.jar in " +
                        karafHomePath + "/system");
            }
            // look for Google Guava library in guava-*.jar
            File guavaLib = findVersionFile(systemDir, Pattern.compile("guava-[0-9\\.]*\\.jar"));
            if (guavaLib == null) {
                throw new InvalidPluginConfigurationException(
                        "Missing Google Guava library guava-*.jar in " + karafHomePath + "/system");
            }
            classpath.add(guavaLib.toURI().toURL());
        }
        classpath.add(zooKeeperLib.toURI().toURL());

        // add optional jgit library bundled in fabric-git for 6.1
        final File jGitLib = findVersionFile(systemDir, Pattern.compile("fabric-git-1.*\\.jar"));

        if (jGitLib != null) {
            classpath.add(jGitLib.toURI().toURL());
        } else {
            final String gitRepoPath = details.getPluginConfiguration().getSimpleValue(GIT_REPO_PROPERTY);
            final File gitRepo = gitRepoPath != null ? new File(gitRepoPath) : null;
            if (gitRepo != null && gitRepo.exists() && gitRepo.isDirectory()) {
                // git repo exists, but could not find jgit!
                throw new InvalidPluginConfigurationException("Missing JGit library fabric-git-1*.jar in " +
                        karafHomePath + "/system");
            }
        }

        return classpath;
    }

    /* Mostly lifted as is from the AS7 plugin, courtesy of Heiko Rupp and Ian Springer */
    private void setStartScriptPluginConfigProps(ProcessInfo process, JavaCommandLine commandLine,
                                                 Configuration pluginConfig, ProcessInfo agentProcess) {
        StartScriptConfiguration startScriptConfig = new StartScriptConfiguration(pluginConfig);
        ProcessInfo parentProcess = process.getParentProcess();

        File startScript = ServerStartScriptDiscoveryUtility.getStartScript(parentProcess);
        if (startScript == null) {
            // The parent process is not a script - fallback to the default value (e.g. "bin/start").
            String startScriptFileName = (OS_IS_WINDOWS ? "start.bat" : "start");
            startScript = new File("bin", startScriptFileName);
        }
        if (!startScript.exists()) {
            if (!startScript.isAbsolute()) {
                File homeDir = new File(pluginConfig.getSimpleValue("karaf.base"));
                File startScriptAbsolute = new File(homeDir, startScript.getPath());
                if (!startScriptAbsolute.exists()) {
                    log.warn("Failed to find start script file for JBoss Fuse server with command line [" + commandLine
                        + "] - defaulting 'startScripFile' plugin config prop to [" + startScript + "].");
                }
            }
        }
        startScriptConfig.setStartScript(startScript);

        String startScriptPrefix = ServerStartScriptDiscoveryUtility.getStartScriptPrefix(process, agentProcess);
        startScriptConfig.setStartScriptPrefix(startScriptPrefix);

        Map<String, String> startScriptEnv = ServerStartScriptDiscoveryUtility.getStartScriptEnv(process, parentProcess,
            START_SCRIPT_ENV_VAR_NAMES);
        startScriptConfig.setStartScriptEnv(startScriptEnv);

        List<String> startScriptArgs = ServerStartScriptDiscoveryUtility.getStartScriptArgs(parentProcess,
            commandLine.getClassArguments(), START_SCRIPT_OPTION_EXCLUDES);
        startScriptConfig.setStartScriptArgs(startScriptArgs);
    }

}
