org.codice.ddf.admin.application.service.command.ProfileInstallCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.admin.application.service.command.ProfileInstallCommand.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>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 Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.admin.application.service.command;

import com.google.gson.reflect.TypeToken;
import ddf.security.common.audit.SecurityLogger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.karaf.bundle.core.BundleService;
import org.apache.karaf.features.Dependency;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.codice.ddf.admin.application.service.ApplicationService;
import org.codice.ddf.admin.application.service.ApplicationServiceException;
import org.codice.ddf.admin.application.service.migratable.JsonUtils;
import org.osgi.framework.BundleException;
import org.osgi.service.resolver.ResolutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Command(scope = "profile", name = "install", description = "Installs the profile")
@Service
public class ProfileInstallCommand extends AbstractProfileCommand {
    @Argument(index = 0, name = "profileName", description = "Name of install profile to use", required = true, multiValued = false)
    String profileName;

    private static final String ADVANCED_PROFILE_INSTALL_FEATURES = "install-features";
    private static final String ADVANCED_PROFILE_UNINSTALL_FEATURES = "uninstall-features";
    private static final String ADVANCED_PROFILE_STOP_BUNDLES = "stop-bundles";
    private static final String FEATURE_FAILURE_MESSAGE = "Feature: %s does not exist";
    private static final EnumSet NO_AUTO_REFRESH = EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles);
    private static final String PROFILE_PREFIX = "profile-";
    private static final String RESTART_WARNING = "An unexpected error occurred during the installation process. The system is in unknown state. It is strongly recommended to restart the installation from the beginning.";
    private static final Logger LOGGER = LoggerFactory.getLogger(ProfileInstallCommand.class);

    @Override
    protected final void doExecute(ApplicationService applicationService, FeaturesService featuresService,
            BundleService bundleService) throws Exception {

        profileName = profileName.trim();

        if (profileName.startsWith(".") || profileName.startsWith("/")
                || profileName.matches("((?i)(?s)[A-Z]):.*")) {
            throw new IllegalArgumentException(
                    "Profile Name must not start with '.', '/', or a windows drive letter");
        }

        try {
            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
                installProfile(applicationService, featuresService, bundleService, profileName);
                return null;
            });
        } catch (PrivilegedActionException e) {
            throw e.getException();
        }
    }

    private void installProfile(ApplicationService applicationService, FeaturesService featuresService,
            BundleService bundleService, String profileName) throws Exception {
        Optional<Feature> optionalProfile = getProfile(applicationService, profileName);
        Optional<Map<String, List<String>>> optionalExtraProfiles = getProfile(profileName);

        try {
            if (optionalProfile.isPresent()) {
                List<String> profileApps = optionalProfile.get().getDependencies().stream().map(Dependency::getName)
                        .collect(Collectors.toList());
                installFeatures(featuresService, profileApps);
                uninstallInstallerModule(featuresService);
                printSuccess("Installation Complete");
            } else {
                if (optionalExtraProfiles.isPresent()) {
                    Map<String, List<String>> profile = optionalExtraProfiles.get();
                    installFeatures(featuresService, profile.get(ADVANCED_PROFILE_INSTALL_FEATURES));
                    uninstallFeatures(featuresService, profile.get(ADVANCED_PROFILE_UNINSTALL_FEATURES));
                    /* The stop-bundles operation is a workaround currently in place for dealing with
                    the inter-dependencies of some features, this will likely be removed in the future. */
                    stopBundles(bundleService, profile.get(ADVANCED_PROFILE_STOP_BUNDLES));
                    uninstallInstallerModule(featuresService);
                    printSuccess("Installation Complete");
                    SecurityLogger.audit("Installed profile: {}", profile);
                } else {
                    printError(String.format("Profile: %s not found", profileName));
                }
            }
        } catch (ApplicationServiceException | ResolutionException | BundleException | IllegalArgumentException e) {
            SecurityLogger.audit("Failed to install profile: {}", profileName);
            printError(RESTART_WARNING);
            throw e;
        }
    }

    private void uninstallInstallerModule(FeaturesService featuresService) throws Exception {
        try {
            if (!featuresService.isInstalled(featuresService.getFeature("admin-post-install-modules"))) {
                printSectionHeading("Finalizing Installation");
                installFeature(featuresService, "admin-post-install-modules");
            }
            if (featuresService.isInstalled(featuresService.getFeature("admin-modules-installer"))) {
                uninstallFeature(featuresService, featuresService.getFeature("admin-modules-installer"));
            }
        } catch (Exception e) {
            printError("An error occurred while trying to perform post-install operations");
            throw e;
        }
    }

    private void installFeatures(FeaturesService featuresService, List<String> installFeatures) throws Exception {
        if (installFeatures != null) {
            printSectionHeading("Installing Features");
            Set<String> uniqueValues = new HashSet<>();
            for (String feature : installFeatures) {
                if (uniqueValues.add(feature)) {
                    printItemStatusPending("Installing: ", feature);
                    installFeature(featuresService, feature);
                    printItemStatusSuccess("Installed: ", feature);
                }
            }
        }
    }

    private void uninstallFeatures(FeaturesService featuresService, List<String> uninstallFeatures)
            throws Exception {
        if (uninstallFeatures != null) {
            printSectionHeading("Uninstalling Features");
            Set<String> uniqueValues = new HashSet<>();
            for (String feature : uninstallFeatures) {
                if (uniqueValues.add(feature)) {
                    printItemStatusPending("Uninstalling: ", feature);
                    Feature featureObject = null;
                    try {
                        featureObject = featuresService.getFeature(feature);
                    } catch (Exception e) {
                        printError(String.format(FEATURE_FAILURE_MESSAGE, feature));
                        throw e;
                    }
                    if (featureObject == null) {
                        printItemStatusFailure("Uninstall Failed: ", feature);
                        printError(String.format(FEATURE_FAILURE_MESSAGE, feature));
                        throw new IllegalArgumentException(String.format(FEATURE_FAILURE_MESSAGE, feature));
                    }
                    uninstallFeature(featuresService, featureObject);
                    printItemStatusSuccess("Uninstalled: ", feature);
                }
            }
        }
    }

    private void stopBundles(BundleService bundleService, List<String> stopBundleNames)
            throws BundleException, IllegalArgumentException {
        if (stopBundleNames != null) {
            printSectionHeading("Stopping Bundles");
            Set<String> uniqueValues = new HashSet<>();
            for (String bundle : stopBundleNames) {
                if (uniqueValues.add(bundle)) {
                    printItemStatusPending("Stopping: ", bundle);
                    stopBundle(bundleService, bundle);
                    printItemStatusSuccess("Stopped: ", bundle);
                }
            }
        }
    }

    /**
     * Get the {@link Feature} that makes up a profile
     *
     * @param applicationService {@link ApplicationService}
     * @param profileName Name of profile to get
     * @return {@link Optional} if the profile doesn't exist the {@link Optional} will be empty
     */
    private Optional<Feature> getProfile(ApplicationService applicationService, String profileName) {
        return applicationService.getInstallationProfiles().stream()
                .filter(i -> i.getName().replaceFirst(PROFILE_PREFIX, "").equals(profileName)).findFirst();
    }

    /**
     * Gets the content of an advanced profile
     *
     * @param profileName Name of profile to get
     * @return {@link Optional}, if the profile doesn't exist the {@link Optional} will be empty
     */
    private Optional<Map<String, List<String>>> getProfile(String profileName) {
        File profileFile = Paths.get(profilePath.toString(), profileName + PROFILE_EXTENSION).toFile();
        Map<String, List<String>> profileMap = null;

        try {
            profileMap = (Map<String, List<String>>) JsonUtils.fromJson(
                    FileUtils.readFileToString(profileFile, StandardCharsets.UTF_8),
                    new TypeToken<Map<String, List<String>>>() {
                    }.getType());
            SecurityLogger.audit("Read profile {} from {}", profileName, profileFile.getAbsolutePath());
        } catch (FileNotFoundException e) {
            LOGGER.debug("The file associated with profile: {} was not found under {}", profileName, profilePath);
        } catch (IOException e) {
            LOGGER.debug("An IOException occurred: ", e);
        }

        return Optional.ofNullable(profileMap);
    }

    private void installFeature(FeaturesService featuresService, String feature) throws Exception {
        try {
            featuresService.installFeature(feature, NO_AUTO_REFRESH);
        } catch (Exception e) {
            printItemStatusFailure("Install Failed: ", feature);
            throw e;
        }
    }

    private void uninstallFeature(FeaturesService featuresService, Feature feature) throws Exception {
        try {
            featuresService.uninstallFeature(feature.getName(), feature.getVersion(), NO_AUTO_REFRESH);
        } catch (Exception e) {
            printItemStatusFailure("Uninstall Failed: ", feature.getName());
            throw e;
        }
    }

    private void stopBundle(BundleService bundleService, String bundle) throws BundleException {
        try {
            bundleService.getBundle(bundle).stop();
        } catch (BundleException e) {
            printItemStatusFailure("Stop Failed: ", bundle);
            throw e;
        } catch (IllegalArgumentException ie) {
            printItemStatusFailure("Stop Failed: ", bundle);
            printError(String.format("Bundle: %s does not exist", bundle));
            throw ie;
        }
    }
}