#!/usr/bin/env sh

###
# ============LICENSE_START=======================================================
# ONAP POLICY
# ================================================================================
# Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed 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.
# ============LICENSE_END=========================================================
##

# #############################################################
# Features Directory Layout:
#
# POLICY_HOME/
#   L─ features/
#        L─ <feature-name>*/
#            L─ [config]/
#            |   L─ <config-file>+
#            L─ [bin]/
#            |   L─ <bin-file>+
#            L─ lib/
#            |   L─ [dependencies]/
#            |   |   L─ <dependent-jar>+
#            │   L─ feature/
#            │       L─ <feature-jar>
#            L─ [db]/
#            │   L─ <db-name>/+
#            │       L─ sql/
#            │           L─ <sql-scripts>*
#            L─ [artifacts]/
#                L─ <artifact>+
#            L─ [install]
#                L─ [enable]
#                L─ [disable]
#                L─ [other-directories-or-files]
#
# notes:  [] = optional , * = 0 or more , + = 1 or more
#   <feature-name> directory without "feature-" prefix.
#   [config]       feature configuration directory that contains all configuration
#                  needed for this features
#   [config]/<config-file>  preferable named with "feature-<feature-name>" prefix to
#                  precisely match it against the exact features, source code, and 
#                  associated wiki page for configuration details.
#   [bin]       feature bin directory that contains helper scripts for this feature
#   [bin]/<executable-file>  preferable named with "feature-<feature-name>" prefix.
#   lib            jar libraries needed by this features
#   lib/[dependencies]  3rd party jar dependencies not provided by base installation
#                  of pdp-d that are necessary for <feature-name> to operate
#                  correctly.
#   lib/feature    the single feature jar that implements the feature.
#   [db]           database directory, if the feature contains sql.
#   [db]/<db-name> database to which underlying sql scripts should be applied against.
#                  ideally, <db-name> = <feature-name> so it is easily to associate
#                  the db data with a feature itself.   Ideally, since a feature is
#                  a somewhat independent isolated unit of functionality,the <db-name>
#                  database ideally isolates all its data.
#   [db]/<db-name>/sql  directory with all the sql scripts.
#   [db]/<db-name>/sql/<sql-scripts>  for this feature sql scripts
#                  upgrade scripts should be suffixed with ".upgrade.sql"
#                  downgrade scripts should be suffixed with ".downgrade.sql"
#   [artifacts]    maven artifacts to be deployed in a maven repository.
#   [artifacts]/<artifact>  maven artifact with identifiable maven coordinates embedded
#                  in the artifact.
#   [install]      custom installation directory where custom enable or disable scripts
#                  and other free form data is included to be used for the enable and 
#                  and disable scripts.
#   [install]/[enable] enable script executed when the enable operation is invoked in
#                  the feature.
#   [install]/[disable] disable script executed when the disable operation is invoked in
#                  the feature.
#   [install]/[other-directories-or-files] other executables, or data that can be used
#                  by the feature for any of its operations.   The content is determined
#                  by the feature designer.
#  
# Operations:
#   install: installs a feature
#   uninstall: uninstalls a feature
#   enable : enables 1) dependencies, 2) configuration, 3) binaries 4) database, 5) artifacts,
#                    6) feature, 7) customization.
#   disable: disables 1) dependencies, 2) configuration, 3) binaries, 4) database, 5) feature,
#                     6) customization
#   status : status of a feature
#
# 'enable' operation details:
#  0. Validates current state before the operation is committed
#  1. sets the symbolic link to the actual feature jar in pdp-d classpath ($POLICY_HOME/lib)
#  2. sets symbolic links to feature dependencies in pdp-d classpath ($POLICY_HOME/lib)
#  3. sets symbolic links to feature configuration in pdp-d configuration directory ($POLICY_HOME/config)
#  4. sets symbolic links to feature executables in pdp-d bin directory ($POLICY_HOME/bin)
#  5. sets symbolic links to feature upgrade scripts and removes links to downgrade scripts (if any)
#     in the pdp-d migration directory ($POLICY_HOME/etc/db/migration).
#  6. deploys any maven artifacts in the maven repositories in use (if any)
#  7. cd to the feature 'install' directory an executes (if exists) the 'enable' script to allow for specific
#     customizations for this feature.
#
# 'disable' operation details:
#  0. Validates current state before the operation is committed
#  1. removes the symbolic link to the actual feature jar in pdp-d classpath ($POLICY_HOME/lib)
#  2. removes symbolic links to feature dependencies in pdp-d classpath ($POLICY_HOME/lib)
#  3. removes symbolic links to feature configuration in pdp-d configuration directory ($POLICY_HOME/config)
#  4. removes symbolic links to feature executables in pdp-d bin directory ($POLICY_HOME/bin)
#  5. removes symbolic links to feature upgrade scripts and sets links to downgrade scripts (if any)
#     in the pdp-d migration directory ($POLICY_HOME/etc/db/migration).
#  6. cd to the feature 'install' directory an executes (if exists) the 'disable' script to allow for specific
#     customizations for this feature.
#
# Notes for DB enabled features:
#     A. Upgrade/Downgrade SQL File Name Format:
#             <VERSION>-<pdp|feature-name>[-description](.upgrade|.downgrade).sql
#   B. See related tooling: db-migrator, deploy-artifact, and policy
#
# Example:
#
# POLICY_HOME/
#   L─ features/
#        L── eelf/
#        │   L── config/
#        │   │   L── logback-eelf.xml
#        │   L─ lib/
#        │   │   L─ dependencies/
#        │   │   │   L─ ONAP-Logging-1.1.0-SNAPSHOT.jar
#        │   │   │   L─ eelf-core-1.0.0.jar
#        │   │   L─ feature/
#        │   │       L─ feature-eelf-1.1.0-SNAPSHOT.jar
#        │   L─ install/
#        │       L─ enable
#        │       L─ disable
#        L─ healthcheck/
#            L── config/
#            │   L─ feature-healthcheck.properties
#            L─ lib/
#                L─ feature/
#                    L─ feature-healthcheck-1.1.0-SNAPSHOT.jar
# #############################################################

if [ "${DEBUG}" = "y" ]; then
    echo "-- MAIN --"
    set -x
fi
    
# The directories at play

LIB=${POLICY_HOME}/lib
CONFIG=${POLICY_HOME}/config
BIN=${POLICY_HOME}/bin
DB=${POLICY_HOME}/etc/db/migration
FEATURES=${POLICY_HOME}/features

if [ ! -d "${LIB}" ]; then
    echo "error: no ${LIB} directory"
    exit 1
fi

if [ ! -d "${CONFIG}" ]; then
    echo "error: no ${CONFIG} directory"
    exit 2
fi

# ensure that the directory exists
mkdir -p "${FEATURES}" 2> /dev/null

if [ ! -d "${DB}" ]; then
    mkdir -p "${DB}"
fi

# relative per Feature Directory Paths

FEATURE_DEPS="lib/dependencies"
FEATURE_LIB="lib/feature"
FEATURE_CONFIG="config"
FEATURE_BIN="bin"
FEATURE_INSTALL="install"
FEATURE_ARTIFACTS="artifacts"
FEATURE_DB="db"
FEATURE_SQL="sql"

UPGRADE_SQL_SUFFIX=".upgrade.sql"
DOWNGRADE_SQL_SUFFIX=".downgrade.sql"

featureJars=$(find "${FEATURES}" -name "feature-*.jar" -type f -exec basename {} \; 2> /dev/null)

# default field lengths
nameLength=20
versionLength=15

# update field lengths, if needed
for jar in ${featureJars} ; do
    # get file name without 'jar' suffix
    tmp="${jar%\.jar}"

    # remove feature prefix
    tmp="${tmp#feature-}"
        
    # get feature name by removing the version portion
    name="${tmp%%-[0-9]*}"

    # extract version portion of name
    version="${tmp#${name}-}"

    # grow the size of the name/version field, if needed
    if [ ${#name} -gt $nameLength ] ; then
        nameLength=${#name}
    fi
    if [ ${#version} -gt $versionLength ] ; then
        versionLength=${#version}
    fi
done

# ##########################################################
# usage: usage information
# ##########################################################
function usage
{
		# print out usage information
		cat >&2 <<-'EOF'
		Usage:  features status
		            Get enabled/disabled status on all features
		        features enable <feature> ...
		            Enable the specified feature
		        features disable <feature> ...
		            Disable the specified feature
		        features install [ <feature> | <file-name> ] ...
		            Install the specified feature
		        features uninstall <feature> ...
		            Uninstall the specified feature
		EOF
}


# ##########################################################
# status: dump out status information
# ##########################################################
function status
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- status --"
        set -x
    fi
    
    local tmp name version status
    local format="%-${nameLength}s %-${versionLength}s %s\n"
    
    printf "${format}" "name" "version" "status"
    printf "${format}" "----" "-------" "------"
    
    for jar in ${featureJars} ; do
        # get file name without 'jar' suffix
        tmp="${jar%\.jar}"
        
        # remove feature prefix
        tmp="${tmp#feature-}"

        # get feature name by removing the version portion
        name="${tmp%%-[0-9]*}"

        # extract version portion of name
        version="${tmp#${name}-}"

        # determine status
        status=disabled
        if [ -e "${LIB}/${jar}" ] ; then
            status=enabled
        fi
        printf "${format}" "${name}" "${version}" "${status}"
    done
    echo
}

# ##########################################################
# enableDepAnalysis (featureName):
#                   reports on potential dependency conflicts
#   featureName: name of the feature
# ##########################################################
function enableDepAnalysis ()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableDepAnalysis $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureDepJars featureDepJarPath depJarName multiVersionJars
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
    fi
    
    featureDepJars=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
    for featureDepJarPath in ${featureDepJars}; do
        depJarName=$(basename "${featureDepJarPath}")
        
        # it could be a base jar

        if [ -f "${LIB}"/"${depJarName}" ]; then
            echo "warning: dependency ${depJarName} already in use"
            continue
        fi
        
        # it could be a link from another feature

        if [ -L "${LIB}"/"${depJarName}" ]; then
            continue
        fi
        
        # unadvisable if multiple versions exist but the base or
        # installed one wins.

        multiVersionJars=$(ls "${LIB}"/"${depJarName%%-[0-9]*.jar}"-*.jar 2> /dev/null)
        if [ -n "${multiVersionJars}" ]; then
            echo "warning: other version of library ${depJarName} present: ${multiVersionJars}"
            continue
        fi
    done
}

# ##########################################################
# enableConfigAnalysis (featureName):
#                   reports on potential dependency conflicts
#   featureName: name of the feature
# ##########################################################
function enableConfigAnalysis ()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableConfigAnalysis $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureConfigs configPath configFileName
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureConfigs=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ 2> /dev/null)
    for configPath in ${featureConfigs}; do
        configFileName=$(basename "${configPath}")
        if [ -e "${CONFIG}"/"${configFileName}" ]; then
            echo "error: a config file of the same name is already in the base installation: ${configFileName}"
            return 2
        fi
    done
}

# ##########################################################
# enableBinAnalysis (featureName):
#                   reports on potential dependency conflicts
#   featureName: name of the feature
# ##########################################################
function enableBinAnalysis ()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableBinAnalysis $* --"
        set -x
    fi

    local featureName="$1"
    local featureBins binPath binFileName

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi

    featureBins=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ 2> /dev/null)
    for binPath in ${featureBins}; do
        binFileName=$(basename "${binPath}")
        if [ -e "${CONFIG}"/"${binFileName}" ]; then
            echo "error: a bin file of the same name is already in the base installation: ${binFileName}"
            return 2
        fi
    done
}

# ##########################################################
# enableDbAnalysis (featureName):
#                   reports on potential db access problems
#   featureName: name of the feature
# ##########################################################
function enableDbAnalysis()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableDbAnalysis $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureSqls
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureSqls=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/*/${FEATURE_SQL}/*${UPGRADE_SQL_SUFFIX} 2> /dev/null)
    if [ -z "${featureSqls}" ]; then
        return 0
    fi
    
    source "${POLICY_HOME}"/etc/profile.d/env.sh
    if [ -z "${SQL_HOST}" ] || [ -z "${SQL_USER}" ] || [ -z "${SQL_PASSWORD}" ]; then
        echo "warning: DB server is not configured"
    fi

    return 0
}

# ##########################################################
# enableFeatureDeps(featureName):
#                               enables feature dependencies
#   featureName: name of the feature
# ##########################################################
function enableFeatureDeps()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureDeps $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureDeps featureDepPath depJarName
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
    for featureDepPath in ${featureDeps}; do
        depJarName=$(basename "${featureDepPath}")
        if [ ! -f "${LIB}"/"${depJarName}" ]; then
            ln -s -f "${featureDepPath}" "${LIB}/"
        fi
    done
}

# ##########################################################
# enableFeatureConfig(featureName):
#                               enables feature configuration
#   featureName: name of the feature
# ##########################################################
function enableFeatureConfig()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureConfig $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureConfigs featureConfigPath
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null)
    for featureConfigPath in ${featureConfigs}; do
        ln -s -f "${featureConfigPath}" "${CONFIG}/"
    done
}

# ##########################################################
# enableFeatureBin(featureName): enables feature binaries
#   featureName: name of the feature
# ##########################################################
function enableFeatureBin()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureBin $* --"
        set -x
    fi

    local featureName="$1"
    local featureBins featureBinPath

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi

    featureBins=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ -type f -maxdepth 1 2> /dev/null)
    for featureBinPath in ${featureBins}; do
        chmod u+x "${featureBinPath}"
        ln -s -f "${featureBinPath}" "${BIN}/"
    done
}

# ##########################################################
# enableFeatureDbSchema(featureName):
#        enables feature DB Schema configuration
#   featureName: name of the feature
# ##########################################################
function enableFeatureDbSchema()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureDbSchema $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureDbPath="$2"
    local schemaName="$3"

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    if [ -z "${featureDbPath}" ]; then
        echo "warning: ${featureName} contains no DB path"
        return 2
    fi
    
    if [ -z "${schemaName}" ]; then
        echo "warning: feature ${featureName} contains no schema name"
        return 3
    fi
    
    rc=0
    sqlUpgradeScripts=$(ls "${featureDbPath%/}"/${FEATURE_SQL}/*${UPGRADE_SQL_SUFFIX} 2> /dev/null)
    for sqlUpgradeScript in ${sqlUpgradeScripts}; do
        if [ ! -d "${DB}"/"${schemaName}"/${FEATURE_SQL} ]; then
            mkdir -p "${DB}"/"${schemaName}"/${FEATURE_SQL} 2> /dev/null
        fi        
        ln -s -f "${sqlUpgradeScript}" "${DB}"/"${schemaName}"/${FEATURE_SQL}/
    done
    
    sqlDowngradeScripts=$(ls "${featureDbPath%/}"/${FEATURE_SQL}/*${DOWNGRADE_SQL_SUFFIX} 2> /dev/null)
    for sqlDowngradeScript in ${sqlDowngradeScripts}; do
        if [ -d "${DB}"/"${schemaName}"/${FEATURE_SQL} ]; then
            sqlName=$(basename "${sqlDowngradeScript}")
            rm -f "${DB}"/"${schemaName}"/"${FEATURE_SQL}"/"${sqlName}" 2> /dev/null
        else
            echo "warning: feature ${featureName} only contains downgrade scripts"
            rc=4
            break
        fi
    done
    
    if [ -n "${sqlUpgradeScripts}" ] || [ -n "${sqlDowngradeScripts}" ]; then
        DEBUG=${DEBUG} db-migrator -s "${schemaName}" -o ok
    fi
    
    return ${rc}
}

# ##########################################################
# enableFeatureDb(featureName):
#       enables DB feature configuration
#   featureName: name of the feature
# ##########################################################
function enableFeatureDb()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureDb $* --"
        set -x
    fi
    
    local featureName="$1" 
    local featureDbs featureDbPath schemaName sqls 
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureDbs=$(ls -d "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/*/ 2> /dev/null)
    for featureDbPath in ${featureDbs}; do
        sqls=$(ls "${featureDbPath%/}"/"${FEATURE_SQL}"/*.sql 2> /dev/null)
        if [ -z "${sqls}" ]; then
            continue
        fi
        schemaName=$(basename "${featureDbPath%/}")
        enableFeatureDbSchema "${featureName}" "${featureDbPath%/}" "${schemaName}"
    done
}

# ##########################################################
# enableFeatureArtifacts(featureName):
#   deploys maven artifacts
#       featureName: name of the feature
# ##########################################################
function enableFeatureArtifacts()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- enableFeatureArtifacts $* --"
        set -x
    fi

    local featureName="$1"
    local artifacts

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi

    artifacts=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_ARTIFACTS}"/* 2> /dev/null)
    for artifactPath in ${artifacts}; do
        deploy-artifact -l -a "${artifactPath}"
    done
}

# ##########################################################
# customize(featureName):
#    executes customized script for an operation.
#
# featureName - feature name
# operation - operation, ie.
#  'enable', 'disable', 'install', or 'uninstall'
# ##########################################################
function customOpScript()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- customOpScript $* --"
        set -x
    fi
    
    local featureName="$1"
    local operation="$2"
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    if [ -z "${operation}" ]; then
        echo "warning: ${featureName} : a custom operation script must be provided"
        return 1
    fi
    
    local customScript="${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"/"${operation}"
    if [ -f "${customScript}" ]; then
            cd "${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"
            chmod u+x "${customScript}"
            ./"$(basename "${customScript}")"
    fi
}

# ##########################################################
# enableFeature(featureName, featureJar):  enables a feature
#   featureName: name of the feature
#   featureJar:  path to feature jar implementation
# ##########################################################
function enableFeature()
{
    if [ "$DEBUG" = "y" ]; then
        echo "-- enableFeature $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureJar="$2"
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    if [ -z "${featureJar}" ]; then
        echo "warning: no feature jar"
        return 2
    fi
    
    if ! enableDepAnalysis  "${featureName}"; then
        return "$?"
    fi
    
    if ! enableConfigAnalysis  "${featureName}"; then
        return "$?"
    fi
    
    if ! enableBinAnalysis  "${featureName}"; then
        return "$?"
    fi

    if ! enableDbAnalysis "${featureName}"; then
        return "$?"
    fi
    
    # enable feature itself

    ln -s -f "${featureJar}" "${LIB}/"
        
    # enable dependent libraries if any
    
    enableFeatureDeps "${featureName}"
    
    # enable configuration

    enableFeatureConfig "${featureName}"

    # enable binaries

    enableFeatureBin "${featureName}"

    # enable db

    enableFeatureDb "${featureName}"

    # enable feature artifacts

    enableFeatureArtifacts "${featureName}"

    # run custom enable if any

    customOpScript "${featureName}" "enable"
}

# ##########################################################
# disableFeatureDeps(featureName):
#            disables feature dependencies
# ##########################################################
function disableFeatureDeps()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeatureDeps $* --"
        set -x
    fi
    
    local featureName="$1"
    local aDepsEnabledMap xDepsEnabledMap featureBaseDirs aFeatureDir aFeatureName
    local featureDeps aFeatureDep
    local depJarPath depJarName depJarRealPath

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureBaseDirs=$(ls -d "${FEATURES}"/*/ 2> /dev/null)
    for aFeatureDir in ${featureBaseDirs}; do
        aFeatureName=$(basename "${aFeatureDir}")
        if [ "${aFeatureName}" = "${featureName}" ]; then
            continue
        fi
        
        depJarPaths=$(ls "${aFeatureDir}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
        for depJarPath in ${depJarPaths}; do
            if [ "$?" = "0" ] ; then
                depJarName=$(basename "${depJarPath}")
                xDepsEnabledMap="${xDepsEnabledMap} ${depJarPath}"
            fi
        done
    done

    if [ "${DEBUG}" = "y" ]; then
        echo "${xDepsEnabledMap}"
    fi
    
    featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
    for aFeatureDep in ${featureDeps}; do
        depJarName=$(basename "${aFeatureDep}")
        #
        # check if defJarName is a sym-link
        #  
        if [ -L "${LIB}"/"${depJarName}" ]; then
            depJarRealPath=$(readlink -f "${LIB}"/"${depJarName}")
            if [ "${depJarRealPath}" = "${aFeatureDep}" ]; then
                rm -f "${LIB}"/"${depJarName}"
                
                # case there were multiple features using this library
                # re-enable link fron an enabled feature
                for aDepsEnabledMap in ${xDepsEnabledMap}; do
                   if [ $(basename "${aDepsEnabledMap}") = ${depJarName} ]; then
                      ln -s -f "${aDepsEnabledMap}" "${LIB}/"
                   fi
                done                 
            fi
        fi        
    done
}

# ##########################################################
# disableFeatureConfig(featureName):
#                               disables feature configuration
#   featureName: name of the feature
# ##########################################################
function disableFeatureConfig()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeatureConfig $* --"
        set -x
    fi
    
    local featureName="$1"
    local featureConfigs featureConfigPath
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null)
    for featureConfigPath in ${featureConfigs}; do
        configFileName=$(basename "${featureConfigPath}")
        rm -f "${CONFIG}"/"${configFileName}" 2> /dev/null
    done
}

# ##########################################################
# disableFeatureBin(featureName):
#                               disables feature binaries
#   featureName: name of the feature
# ##########################################################
function disableFeatureBin()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeatureBin $* --"
        set -x
    fi

    local featureName="$1"
    local featureBins featureBinPath

    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi

    featureBins=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ -type f -maxdepth 1 2> /dev/null)
    for featureBinPath in ${featureBins}; do
        binFileName=$(basename "${featureBinPath}")
        rm -f "${BIN}"/"${binFileName}" 2> /dev/null
    done
}

# ##########################################################
# disableFeatureDbSchema(featureName, featureDbPath, schemaName):
#                 disables feature db configuration for a schema
#   featureName: name of the feature
# ##########################################################
function disableFeatureDbSchema()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeatureDbSchema $* --"
        set -x
    fi
    
    local featureName="$1" featureDbPath="$2" schemaName="$3"
    local upgradeFeatureSqls downgradeFeatureSqls featureSql sqlDir sqlName schemaDir schemaName
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    if [ -z "${featureDbPath}" ]; then
        echo "warning: ${featureName} contains no DB path"
        return 2
    fi
    
    if [ -z "${schemaName}" ]; then
        echo "warning: feature ${featureName} contains no schema name"
        return 3
    fi
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    upgradeFeatureSqls=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/"${schemaName}"/"${FEATURE_SQL}"/*"${UPGRADE_SQL_SUFFIX}" -type f -maxdepth 1 2> /dev/null)
    for featureSql in ${upgradeFeatureSqls}; do
        sqlName=$(basename "${featureSql}")
        sqlDir=$(dirname "${featureSql}")
        schemaDir=$(dirname "${sqlDir}")
        schemaName=$(basename "${schemaDir}")
        rm -f "${DB}"/"${schemaName}"/"${FEATURE_SQL}"/"${sqlName}" 2> /dev/null
    done
    
    downgradeFeatureSqls=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/"${schemaName}"/"${FEATURE_SQL}"/*"${DOWNGRADE_SQL_SUFFIX}" -type f -maxdepth 1 2> /dev/null)
    for featureSql in ${downgradeFeatureSqls}; do
        sqlName=$(basename "${featureSql}")
        sqlDir=$(dirname "${featureSql}")
        schemaDir=$(dirname "${sqlDir}")
        schemaName=$(basename "${schemaDir}")
        if [ ! -d "${DB}"/"${schemaName}"/${FEATURE_SQL} ]; then
            mkdir -p "${DB}"/"${schemaName}"/${FEATURE_SQL} 2> /dev/null
        fi        
        ln -s -f "${featureSql}" "${DB}"/"${schemaName}"/${FEATURE_SQL}/
    done
    
    if [ -n "${sqlUpgradeScripts}" ] || [ -n "${sqlDowngradeScripts}" ]; then
        DEBUG=${DEBUG} db-migrator -s "${schemaName}" -o ok
    fi
}

# ##########################################################
# disableFeatureDb(featureName):
#                 disables feature db configuration
#   featureName: name of the feature
# ##########################################################
function disableFeatureDb()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeatureDb $* --"
        set -x
    fi

    local featureName="$1"
    local featureDbPath featureDbs schemaName
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureDbs=$(ls -d "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/*/ 2> /dev/null)
    for featureDbPath in ${featureDbs}; do
        if [ -z "$(ls "${featureDbPath%/}"/"${FEATURE_SQL}"/*${UPGRADE_SQL_SUFFIX} 2> /dev/null)" ]; then
            continue
        fi
        schemaName=$(basename "${featureDbPath%/}")
        disableFeatureDbSchema "${featureName}" "${featureDbPath%/}" "${schemaName}"
    done
}

# ##########################################################
# disableFeature(featureName):  disables a feature
#   featureName: name of the feature
# ##########################################################
function disableFeature()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- disableFeature $* --"
        set -x
    fi
    
    local featureName="$1"
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return
    fi
    
    # disable feature itself

    (
    cd "${LIB}"
    rm -f feature-"${featureName}"-[0-9]*.jar 2> /dev/null
    )
        
    # disable dependencies if any

    disableFeatureDeps "${featureName}"
    
    # disable configuration if any

    disableFeatureConfig "${featureName}"
    
    # disable binaries if any

    disableFeatureBin "${featureName}"

    # disable DB SQL scripts if any

    disableFeatureDb "${featureName}"

    # run custom disable if any

    customOpScript "${featureName}" "disable"
}

############################################################
# configureFeature <config-file> <features-root-directory>
#
# This was copied from 'policy-drools/docker-install.sh'
# in the 'docker' repository, and modified where needed.
############################################################
function configureFeature()
{
    if [ "$DEBUG" = "y" ]; then
        echo "-- configureFeature $* --"
        set -x
    fi

    local envConfig=$1 featureRoot=$2
    local sedLine="sed -i"
    local sedFiles="" nonBinaryFiles sedFile name value
        
    while read line || [ -n "${line}" ]; do
        if [ -n "${line}" ] && [ "${line:0:1}" != \# ]; then
            name="${line%%=*}"
            value="${line#*=}"
            value=$(echo "${value}" | sed -e 's/[\/&]/\\&/g')
            if [ -z "${name}" ] || [ -z "${value}" ]; then
                echo "warning: ${line} missing name or value"
            fi
            sedLine+=" -e 's/\${{${name}}}/${value}/g' "
        fi
    done < "${envConfig}"
    
    nonBinaryFiles=$(find "${featureRoot}" -type f -exec grep -Iq . {} \; -print 2> /dev/null)
    for sedFile in ${nonBinaryFiles}; do
        if fgrep -l '${{' ${sedFile} > /dev/null 2>&1; then
            sedFiles+="${sedFile} "
        fi
    done

    if [ -n "${sedFiles}" ]; then
        sedLine+=${sedFiles}
        eval "${sedLine}"
    fi
}

############################################################
# installFeatures <feature-name-or-zip-file> ...
#
# This was copied from 'policy-drools/docker-install.sh'
# in the 'docker' repository, and modified where needed.
############################################################
function installFeatures
{
    if [ "$DEBUG" = "y" ]; then
        echo "-- installFeatures $* --"
        set -x
    fi

    local name featureConf feature conf
    if [ -d "${FEATURES}" ]; then
        SOURCE_DIR=$PWD
        for feature in "$@" ; do
            name="${feature}"
            feature=$(ls -v feature-"${name}"-[0-9]*.zip 2>/dev/null|tail -1)
            if [ ! -f "${feature}" ] ; then
                # include the file name in the error message, unless we don't
                # have one -- in this case, use the feature name
                echo "error: feature file ${feature:-for ${name}} not found"
                continue
            fi
            if [ -d "${FEATURES}/${name}" ] ; then
                echo "error: feature ${name} has already been installed"
                continue
            fi

            # extract contents of ZIP file in to feature directory
            mkdir -p "${FEATURES}/${name}" > /dev/null 2>&1
            (cd "${FEATURES}/${name}"; jar xf "${SOURCE_DIR}"/"${feature}")

            # if there is a configuration file available,
            # use it to configure the feature
            featureConf="${dir:+$dir/}feature-${name}.conf"
            if [ -r "${featureConf}" ]; then
                configureFeature "${featureConf}" "${FEATURES}"/"${name}"
                cp "${featureConf}" "${POLICY_HOME}"/etc/profile.d
                echo "feature ${name} has been installed (configuration present)"
            else
                echo "feature ${name} has been installed (no configuration present)"
            fi
                    
            customOpScript "${name}" "install"
        done
        
        # check the current directory and the 'config' directory for a
        # 'base.conf' file -- use the first one that is found
        for conf in base.conf ${POLICY_HOME}/config/base.conf ${POLICY_HOME}/etc/profile.d/base.conf; do
            if [ -f "${conf}" ] ; then
                echo "applying base configuration '${conf}' to features"
                configureFeature "${conf}" "${FEATURES}"
                break
            fi
        done
    else
        echo "error: aborting -- ${FEATURES} is not accessible"
        exit 1
    fi
}

# ##########################################################
# uninstallFeatureDb(featureName):
#                 uninstalls the feature db configuration
#   featureName: name of the feature
# ##########################################################
function uninstallFeatureDb()
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- uninstallFeatureDb $* --"
        set -x
    fi

    local featureName="$1"
    local featureSqls sqlDir sqlName schemaDir schemaName schemaNames leftSqls
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return 1
    fi
    
    featureSqls=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_DB}"/*/${FEATURE_SQL}/*.sql -type f -maxdepth 1 2> /dev/null)
    for featureSql in ${featureSqls}; do
        sqlName=$(basename "${featureSql}")
        sqlDir=$(dirname "${featureSql}")
        schemaDir=$(dirname "${sqlDir}")
        schemaName=$(basename "${schemaDir}")
        schemaNames="${schemaNames} ${schemaName}"
        rm -f "${DB}"/"${schemaName}"/"${FEATURE_SQL}"/"${sqlName}" 2> /dev/null
    done
    for schemaName in ${schemaNames};
    do
        leftSqls=$(ls "${DB}"/"${schemaName}"/"${FEATURE_SQL}"/*.sql 2> /dev/null)
        if [ -n "${leftSqls}" ]; then
            if ! DEBUG=${DEBUG} db-migrator -s "${schemaName}" -o ok; then
                echo -n "warning: ${featureName}: ${schemaName}: database data is leftover. "
                echo -n "Consider cleaning left over data with 'db-migrator'."
            fi
        fi        
    done
}

############################################################
# uninstallFeature <feature-name> ...
############################################################
function uninstallFeature
{
    if [ "$DEBUG" = "y" ]; then
        echo "-- uninstallFeature $* --"
        set -x
    fi
    
    local featureName="$1"
    
    if [ -z "${featureName}" ]; then
        echo "warning: no feature name"
        return
    fi
    disableFeature "${featureName}"
    uninstallFeatureDb "${featureName}"
    customOpScript "${featureName}" "uninstall"
    
    if [ -n "${FEATURES}" ] && [ -n "${featureName}" ]; then
        rm -rf "${FEATURES:-???}/${featureName}"
    fi
}

############################################################
# uninstallFeatures <feature-name> ...
############################################################
function uninstallFeatures
{
    if [ "${DEBUG}" = "y" ]; then
        echo "-- uninstallFeatures --"
        set -x
    fi
    
    local name
    local allFeatures=$(cd ${FEATURES};ls)
    local aFeature
    local flag
    for name in "$@" ; do
        flag=false
        # the following check takes care of potentially troublesome names
        # like '.', '..', and names containing '/'
        for aFeature in ${allFeatures};
        do
            if [ "${aFeature}" = "${name}" ] ; then
                uninstallFeature "${name}"
                echo "feature ${name} uninstalled"
                flag=true
            fi
        done
        if ! $flag; then
            echo "error: feature ${name} not found"
        fi
    done
}

case "$1" in
    status)
    {
        # dump out status information
        status
    };;

    enable)
    {
        if [ -f "${POLICY_HOME}"/PID ]; then
            echo "error: enable: not allowed when policy is running .."
            echo
            status
            exit 10
        fi
        
        # enable the specified options
        shift
        match=
        for name in "$@" ; do
            # look for matches - 'file' has the full path name
            file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null)
            if [ "$?" != "0" ] ; then
                # no matching file
                echo "enable feature: ${name} failed"
            else
                # make sure there is only one feature jar
                countFeatureJars=$(echo "${file}" | wc -w)
                if [ ${countFeatureJars} -ne 1 ]; then
                    echo "warning: skipping ${name},  ${countFeatureJars} feature libraries found"
                    continue
                fi            
                
                # found a match (handle multiple matches, just in case)
                match=true
                
                enableFeature "${name}" "${file}"
            fi
        done
        if [ "${match}" ] ; then
            echo
            status
        fi
    };;

    disable)
    {
        if [ -f "${POLICY_HOME}"/PID ]; then
            echo "error: disable: not allowed when policy is running .."
            echo
            status
            exit 11
        fi
        
        # disable the specified options
        shift
        match=
        for name in "$@" ; do
            # look for matches -- 'file' has the last segment of the path name
            file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null)
            if [ "$?" != "0" ] ; then
                echo "${name}:  no such option"
            else
                # found a match (handle multiple matches, just in case)
                match=true
                
                disableFeature "${name}"
            fi
        done
        if [ "${match}" ] ; then
            echo
            status
        fi
    };;

    install)
    {
        shift
        installFeatures "$@"
    };;

    uninstall)
    {
        if [ -f "${POLICY_HOME}"/PID ]; then
            echo "error: uninstall: not allowed when policy is running .."
            echo
            status
            exit 12
        fi
        shift
        uninstallFeatures "$@"
    };;

    *)
    {
        usage
    };;
esac
exit