All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.deployment.admin.DeployCommand Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright 2016-2023 Payara Foundation and/or its affiliates

package org.glassfish.deployment.admin;

import fish.payara.nucleus.hotdeploy.HotDeployService;
import fish.payara.nucleus.hotdeploy.ApplicationState;
import com.sun.enterprise.config.serverbeans.*;
import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.enterprise.deploy.shared.FileArchive;
import com.sun.enterprise.util.LocalStringManagerImpl;
import fish.payara.enterprise.config.serverbeans.DeploymentGroup;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.inject.Inject;
import org.glassfish.admin.payload.PayloadImpl;
import org.glassfish.api.ActionReport;
import org.glassfish.api.ActionReport.ExitCode;
import org.glassfish.api.I18n;
import org.glassfish.api.admin.AccessRequired.AccessCheck;
import org.glassfish.api.admin.AdminCommand;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.api.admin.AdminCommandSecurity;
import org.glassfish.api.admin.CommandRunner;
import org.glassfish.api.admin.ExecuteOn;
import org.glassfish.api.admin.ParameterMap;
import org.glassfish.api.admin.Payload;
import org.glassfish.api.admin.RestEndpoint;
import org.glassfish.api.admin.RestEndpoints;
import org.glassfish.api.admin.RestParam;
import org.glassfish.api.admin.RuntimeType;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.deployment.archive.ArchiveHandler;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.Events;
import org.glassfish.config.support.CommandTarget;
import org.glassfish.config.support.TargetType;
import org.glassfish.deployment.common.*;
import org.glassfish.deployment.versioning.VersioningService;
import org.glassfish.deployment.versioning.VersioningSyntaxException;
import org.glassfish.deployment.versioning.VersioningUtils;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.data.ApplicationInfo;
import org.glassfish.internal.deployment.*;
import org.glassfish.internal.deployment.analysis.DeploymentSpan;
import org.glassfish.internal.deployment.analysis.SpanSequence;
import org.glassfish.internal.deployment.analysis.StructuredDeploymentTracing;
import org.jvnet.hk2.annotations.Contract;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.config.Transaction;

/**
 * Deploy command
 *
 * @author Jerome Dochez
 */
@Service(name = "deploy")
@I18n("deploy.command")
@PerLookup
@ExecuteOn(value = {RuntimeType.DAS})
@TargetType(value = {CommandTarget.DOMAIN, CommandTarget.DAS, CommandTarget.STANDALONE_INSTANCE, CommandTarget.CLUSTER, CommandTarget.DEPLOYMENT_GROUP})
@RestEndpoints({
    @RestEndpoint(configBean = Applications.class, opType = RestEndpoint.OpType.POST, path = "deploy"),
    @RestEndpoint(configBean = Cluster.class, opType = RestEndpoint.OpType.POST, path = "deploy", params = {
        @RestParam(name = "target", value = "$parent")
    }),
    @RestEndpoint(configBean = DeploymentGroup.class, opType = RestEndpoint.OpType.POST, path = "deploy", params = {
        @RestParam(name = "target", value = "$parent")
    }),
    @RestEndpoint(configBean = Server.class, opType = RestEndpoint.OpType.POST, path = "deploy", params = {
        @RestParam(name = "target", value = "$parent")
    })
})
public class DeployCommand extends DeployCommandParameters implements AdminCommand, EventListener,
        AdminCommandSecurity.Preauthorization, AdminCommandSecurity.AccessCheckProvider {

    final private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(DeployCommand.class);

    @Inject
    Applications apps;

    @Inject
    ServerEnvironment env;

    @Inject
    ServiceLocator habitat;

    @Inject
    CommandRunner commandRunner;

    @Inject
    Deployment deployment;

    @Inject
    SnifferManager snifferManager;

    @Inject
    ArchiveFactory archiveFactory;

    @Inject
    Domain domain;

    @Inject
    Events events;

    @Inject
    VersioningService versioningService;

    @Inject
    private HotDeployService hotDeployService;

    private File safeCopyOfApp = null;
    private File safeCopyOfDeploymentPlan = null;
    private File safeCopyOfAltDD = null;
    private File safeCopyOfRuntimeAltDD = null;
    private File originalPathValue;
    private List previousTargets = new ArrayList<>();
    private final Properties previousVirtualServers = new Properties();
    private final Properties previousEnabledAttributes = new Properties();
    private Logger logger;
    private ExtendedDeploymentContext initialContext;
    private ExtendedDeploymentContext deploymentContext;
    private ArchiveHandler archiveHandler;
    private File expansionDir;
    private ReadableArchive archive;
    private DeploymentTracing timing;
    private transient DeployCommandSupplementalInfo suppInfo;
    private static final String EJB_JAR_XML = "META-INF/ejb-jar.xml";
    private static final String SUN_EJB_JAR_XML = "META-INF/sun-ejb-jar.xml";
    private static final String GF_EJB_JAR_XML = "META-INF/glassfish-ejb-jar.xml";

    private static final String APPLICATION_XML = "META-INF/application.xml";
    private static final String SUN_APPLICATION_XML = "META-INF/sun-application.xml";
    private static final String GF_APPLICATION_XML  = "META-INF/glassfish-application.xml";

    private static final String RA_XML  = "META-INF/ra.xml";

    private static final String APPLICATION_CLIENT_XML = "META-INF/application-client.xml";
    private static final String SUN_APPLICATION_CLIENT_XML = "META-INF/sun-application-client.xml";
    private static final String GF_APPLICATION_CLIENT_XML = "META-INF/glassfish-application-client.xml";
    private StructuredDeploymentTracing structuredTracing;

    public DeployCommand() {
        origin = Origin.deploy;
    }

    @Override
    public boolean preAuthorization(AdminCommandContext context) {
        logger = context.getLogger();
        events.register(this);

        suppInfo
                = new DeployCommandSupplementalInfo();
        context.getActionReport().
                setResultType(DeployCommandSupplementalInfo.class, suppInfo);

        structuredTracing = System.getProperty("org.glassfish.deployment.trace") != null
                ? StructuredDeploymentTracing.create(path.getName())
                : StructuredDeploymentTracing.createDisabled(path.getName());

        timing = new DeploymentTracing(structuredTracing);

        final ActionReport report = context.getActionReport();

        originalPathValue = path;
        if (!path.exists()) {
            report.setMessage(localStrings.getLocalString("fnf", "File not found", path.getAbsolutePath()));
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            return false;
        }
        if (!path.canRead()) {
            report.setMessage(localStrings.getLocalString("fnr", "File {0} does not have read permission", path.getAbsolutePath()));
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            return false;
        }

        if (snifferManager.hasNoSniffers()) {
            String msg = localStrings.getLocalString("nocontainer", "No container services registered, done...");
            report.failure(logger, msg);
            return false;
        }

        try (DeploymentSpan span = structuredTracing.startSpan(DeploymentTracing.AppStage.OPENING_ARCHIVE)) {
            archive = archiveFactory.openArchive(path, this);
        } catch (IOException e) {
            final String msg = localStrings.getLocalString("deploy.errOpeningArtifact",
                    "deploy.errOpeningArtifact", path.getAbsolutePath());
            if (logReportedErrors) {
                report.failure(logger, msg, e);
            } else {
                report.setMessage(msg + path.getAbsolutePath() + e.toString());
                report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            }
            return false;
        }

        if (altdd != null) {
            archive.addArchiveMetaData(DeploymentProperties.ALT_DD, altdd);
        }

        if (runtimealtdd != null) {
            archive.addArchiveMetaData(DeploymentProperties.RUNTIME_ALT_DD,
                    runtimealtdd);
        }

        expansionDir = null;
        deploymentContext = null;
        try(SpanSequence span = structuredTracing.startSequence(DeploymentTracing.AppStage.VALIDATE_TARGET, "command")) {

            deployment.validateSpecifiedTarget(target);

            span.start(DeploymentTracing.AppStage.OPENING_ARCHIVE, "ArchiveHandler");

            archiveHandler = deployment.getArchiveHandler(archive, type);

            if (archiveHandler == null) {
                report.failure(logger, localStrings.getLocalString("deploy.unknownarchivetype", "Archive type of {0} was not recognized", path));
                return false;
            }

            span.start(DeploymentTracing.AppStage.CREATE_DEPLOYMENT_CONTEXT, "Initial");

            Optional appState = hotDeployService.getApplicationState(path);
            boolean hotswap = hotDeploy 
                    && !metadataChanged 
                    && appState.map(ApplicationState::isHotswap).orElse(false);
            if (!hotswap) {
                // create an initial  context
                initialContext = new DeploymentContextImpl(report, archive, this, env);
            } else {
               initialContext = hotDeployService.getApplicationState(path)
                        .map(ApplicationState::getDeploymentContext)
                        .orElseThrow(() -> new RuntimeException());
            }
            initialContext.setArchiveHandler(archiveHandler);

            if (hotDeploy && !metadataChanged && appState.isPresent()) {
                if(!appState.get().start(this, initialContext, events)){
                    appState.get().close();
                    return false;
                }
            } else {
                hotDeployService.removeApplicationState(path);
            }

            structuredTracing.register(initialContext);

            span.finish();

            span.start(DeploymentTracing.AppStage.PROCESS_EVENTS, Deployment.INITIAL_CONTEXT_CREATED.type());
            events.send(new Event(Deployment.INITIAL_CONTEXT_CREATED, initialContext), false);

            span.start(DeploymentTracing.AppStage.DETERMINE_APP_NAME);

            if (!forceName) {
                boolean isModuleDescriptorAvailable = false;
                if (archiveHandler.getArchiveType().equals("ejb")
                        && (archive.exists(EJB_JAR_XML)
                        || archive.exists(SUN_EJB_JAR_XML)
                        || archive.exists(GF_EJB_JAR_XML))) {
                    isModuleDescriptorAvailable = true;
                } else if (archiveHandler.getArchiveType().equals("ear")
                        && (archive.exists(APPLICATION_XML)
                        || archive.exists(SUN_APPLICATION_XML)
                        || archive.exists(GF_APPLICATION_XML))) {
                    isModuleDescriptorAvailable = true;

                } else if (archiveHandler.getArchiveType().equals("car")
                        && (archive.exists(APPLICATION_CLIENT_XML)
                        || archive.exists(SUN_APPLICATION_CLIENT_XML)
                        || archive.exists(GF_APPLICATION_CLIENT_XML))) {
                    isModuleDescriptorAvailable = true;
                } else if (archiveHandler.getArchiveType().equals("rar")
                        && (archive.exists(RA_XML))) {
                    isModuleDescriptorAvailable = true;
                }

                if (isModuleDescriptorAvailable) {
                    name = archiveHandler.getDefaultApplicationName(initialContext.getSource(), initialContext, name);
                }
            }

            if (name == null) {
                name = archiveHandler.getDefaultApplicationName(initialContext.getSource(), initialContext);
            } else {
                DeploymentUtils.validateApplicationName(name);
            }

            boolean isUntagged = VersioningUtils.isUntagged(name);
            // no GlassFish versioning support for OSGi budles
            if (name != null && !isUntagged && type != null && type.equals("osgi")) {
                ActionReport.MessagePart msgPart = context.getActionReport().getTopMessagePart();
                msgPart.setChildrenType("WARNING");
                ActionReport.MessagePart childPart = msgPart.addChild();
                childPart.setMessage(VersioningUtils.LOCALSTRINGS.getLocalString(
                        "versioning.deployment.osgi.warning",
                        "OSGi bundles will not use the GlassFish versioning, any version information embedded as part of the name option will be ignored"));
                name = VersioningUtils.getUntaggedName(name);
            }

            // if no version information embedded as part of application name
            // we try to retrieve the version-identifier element's value from DD
            if (isUntagged) {
                String versionIdentifier = archiveHandler.getVersionIdentifier(initialContext.getSource());

                if (versionIdentifier != null && !versionIdentifier.isEmpty()) {
                    name = name + VersioningUtils.EXPRESSION_SEPARATOR + versionIdentifier;
                }
            }

            if (target == null) {
                target = deployment.getDefaultTarget(name, origin, _classicstyle);
            }

            boolean isRegistered = deployment.isRegistered(name);
            isredeploy = isRegistered && force;
            return true;
        } catch (Exception ex) {
            events.unregister(this);
            if (initialContext != null && initialContext.getSource() != null) {
                try {
                    initialContext.getSource().close();
                } catch (IOException ioex) {
                    throw new RuntimeException(ioex);
                }
            }
            throw new RuntimeException(ex);
        }
    }

    @Override
    public Collection getAccessChecks() {
        final List accessChecks = new ArrayList<>();
        accessChecks.add(new AccessCheck(DeploymentCommandUtils.getResourceNameForApps(domain), "create"));
        accessChecks.add(new AccessCheck(DeploymentCommandUtils.getTargetResourceNameForNewAppRef(domain, target), "create"));

        /*
         * If this app is already deployed then this operation also represents
         * an undeployment - a delete - of that app.
         */
        if (isredeploy) {
            final String appResource = DeploymentCommandUtils.getResourceNameForNewApp(domain, name);
            accessChecks.add(new AccessCheck(appResource, "delete"));
            final String appRefResource = DeploymentCommandUtils.getTargetResourceNameForNewAppRef(domain, target, name);
            accessChecks.add(new AccessCheck(appRefResource, "delete"));
        }

        return accessChecks;
    }

    /**
     * Entry point from the framework into the command execution
     *
     * @param context context for the command.
     */
    @Override
    public void execute(AdminCommandContext context) {
        long timeTakenToDeploy = 0;
        long deploymentTimeMillis = 0;
        Optional appState = Optional.empty();
        final ActionReport report = context.getActionReport();
        try (SpanSequence span = structuredTracing.startSequence(DeploymentTracing.AppStage.VALIDATE_TARGET, "registry")) {

            if (!hotDeploy) {
                hotDeployService.removeApplicationState(initialContext.getSourceDir());
            } else if (!(appState = hotDeployService.getApplicationState(initialContext.getSourceDir())).isPresent()) {
                ApplicationState applicationState = new ApplicationState(name, path, initialContext);
                applicationState.setTarget(target);
                appState = Optional.of(applicationState);
            }

            // needs to be fixed in hk2, we don't generate the right innerclass index. it should use $
            Collection interceptors = habitat.getAllServices(Interceptor.class);
            if (interceptors != null) {
                for (Interceptor interceptor : interceptors) {
                    interceptor.intercept(this, initialContext);
                }
            }

            deployment.validateDeploymentTarget(target, name, isredeploy);

            ActionReport.MessagePart part = report.getTopMessagePart();
            part.addProperty(DeploymentProperties.NAME, name);

            ApplicationConfigInfo savedAppConfig
                    = new ApplicationConfigInfo(apps.getModule(Application.class, name));
            Properties undeployProps = null;
            if (appState.map(ApplicationState::isInactive).orElse(true)) {
                undeployProps = handleRedeploy(name, report, context);
            }
            appState.filter(ApplicationState::isInactive)
                    .ifPresent(hotDeployService::addApplicationState);

            if (enabled == null) {
                enabled = Boolean.TRUE;
            }

            // clean up any left over repository files
            if (!keepreposdir) {
                span.start(DeploymentTracing.AppStage.CLEANUP, "applications");
                final File reposDir = new File(env.getApplicationRepositoryPath(), VersioningUtils.getRepositoryName(name));
                if (reposDir.exists()) {
                    for (int i = 0; i < domain.getApplications().getApplications().size(); i++) {
                        File existrepos = new File(new URI(domain.getApplications().getApplications().get(i).getLocation()));
                        String appname = domain.getApplications().getApplications().get(i).getName();
                        if (!appname.equals(name) && existrepos.getAbsoluteFile().equals(reposDir.getAbsoluteFile())) {
                            report.failure(logger, localStrings.getLocalString("deploy.dupdeployment", "Application {0} is trying to use the same repository directory as application {1}, please choose a different application name to deploy", name, appname));
                            return;
                        }
                    }
                    /*
                     * Delete the repository directory as an archive to allow
                     * any special processing (such as stale file handling)
                     * to run.
                     */
                    final FileArchive arch = DeploymentUtils.openAsFileArchive(reposDir, archiveFactory);
                    arch.delete();
                }
                span.finish();
            }

            if (!DeploymentUtils.isDomainTarget(target) && enabled) {
                // try to disable the enabled version, if exist
                try (SpanSequence innerSpan = span.start(DeploymentTracing.AppStage.SWITCH_VERSIONS)) {
                    versioningService.handleDisable(name, target, report, context.getSubject());
                } catch (VersioningSyntaxException e) {
                    report.failure(logger, e.getMessage());
                    return;
                }
            }

            File source = new File(archive.getURI().getSchemeSpecificPart());
            boolean isDirectoryDeployed = true;
            if (!source.isDirectory()) {
                isDirectoryDeployed = false;
                expansionDir = new File(domain.getApplicationRoot(), VersioningUtils.getRepositoryName(name));
                path = expansionDir;
            } else {
                // test if a version is already directory deployed from this dir
                String versionFromSameDir
                        = versioningService.getVersionFromSameDir(source);
                if (!force && versionFromSameDir != null) {
                    report.failure(logger,
                            VersioningUtils.LOCALSTRINGS.getLocalString(
                                    "versioning.deployment.dual.inplace",
                                    "GlassFish do not support versioning for directory deployment when using the same directory. The directory {0} is already assigned to the version {1}.",
                                    source.getPath(),
                                    versionFromSameDir));
                    return;
                }
            }

            span.start(DeploymentTracing.AppStage.CREATE_DEPLOYMENT_CONTEXT, "Full");
            // create the parent class loader
            deploymentContext
                    = deployment.getBuilder(logger, this, report).
                            source(initialContext.getSource())
                            .archiveHandler(archiveHandler)
                            .build(initialContext);

            // reset the properties (might be null) set by the deployers when undeploying.
            if (undeployProps != null) {
                deploymentContext.getAppProps().putAll(undeployProps);
            }

            if (properties != null || property != null) {
                // if one of them is not null, let's merge them
                // to properties so we don't need to always
                // check for both
                if (properties == null) {
                    properties = new Properties();
                }
                if (property != null) {
                    properties.putAll(property);
                }
            }

            if (properties != null) {
                deploymentContext.getAppProps().putAll(properties);
                validateDeploymentProperties(properties, deploymentContext);
            }

            span.start(DeploymentTracing.AppStage.CLEANUP, "generated");
            // clean up any generated files
            deploymentContext.clean();

            span.start(DeploymentTracing.AppStage.PREPARE, "ServerConfig");
            Properties appProps = deploymentContext.getAppProps();
            /*
             * If the app's location is within the domain's directory then
             * express it in the config as ${com.sun.aas.instanceRootURI}/rest-of-path
             * so users can relocate the entire installation without having
             * to modify the app locations.  Leave the location alone if
             * it does not fall within the domain directory.
             */
            String appLocation = DeploymentUtils.relativizeWithinDomainIfPossible(deploymentContext.getSource().getURI());

            appProps.setProperty(ServerTags.LOCATION, appLocation);
            // set to default "user", deployers can override it
            // during processing
            appProps.setProperty(ServerTags.OBJECT_TYPE, "user");
            if (contextroot != null) {
                appProps.setProperty(ServerTags.CONTEXT_ROOT, contextroot);
            }
            appProps.setProperty(ServerTags.DIRECTORY_DEPLOYED, String.valueOf(isDirectoryDeployed));
            if (type == null) {
                type = archiveHandler.getArchiveType();
            }
            appProps.setProperty(Application.ARCHIVE_TYPE_PROP_NAME, type);
            if (appProps.getProperty(ServerTags.CDI_DEV_MODE_ENABLED_PROP) == null) {
                appProps.setProperty(ServerTags.CDI_DEV_MODE_ENABLED_PROP, Boolean.FALSE.toString());
            }

            savedAppConfig.store(appProps);

            deploymentContext.addTransientAppMetaData(DeploymentProperties.PREVIOUS_TARGETS, previousTargets);
            deploymentContext.addTransientAppMetaData(DeploymentProperties.PREVIOUS_VIRTUAL_SERVERS, previousVirtualServers);
            deploymentContext.addTransientAppMetaData(DeploymentProperties.PREVIOUS_ENABLED_ATTRIBUTES, previousEnabledAttributes);
            Transaction tx = deployment.prepareAppConfigChanges(deploymentContext);
            span.finish(); // next phase is launched by prepare
            Deployment.ApplicationDeployment deplResult = deployment.prepare(null, deploymentContext);
            if (deplResult != null && !loadOnly) {
                appState.ifPresent(s -> s.storeMetaData(deploymentContext));
                // initialize makes its own phase as well
                deployment.initialize(deplResult.appInfo, deplResult.appInfo.getSniffers(), deplResult.context);
            }
            ApplicationInfo appInfo = deplResult != null ? deplResult.appInfo : null;

            /*
             * Various deployers might have added to the downloadable or
             * generated artifacts.  Extract them and, if the command succeeded,
             * persist both into the app properties (which will be recorded
             * in domain.xml).
             */
            final Artifacts downloadableArtifacts
                    = DeploymentUtils.downloadableArtifacts(deploymentContext);
            final Artifacts generatedArtifacts
                    = DeploymentUtils.generatedArtifacts(deploymentContext);

            if (report.getActionExitCode() == ActionReport.ExitCode.SUCCESS) {
                try (SpanSequence innerSpan = span.start(DeploymentTracing.AppStage.REGISTRATION)){
                    moveAppFilesToPermanentLocation(
                            deploymentContext, logger);
                    recordFileLocations(appProps);

                    downloadableArtifacts.record(appProps);
                    generatedArtifacts.record(appProps);

                    timeTakenToDeploy = timing.elapsed();
                    deploymentTimeMillis = System.currentTimeMillis();
                    if (tx != null) {
                        Application application = deploymentContext.getTransientAppMetaData("application", Application.class);
                        // Set the application deploy time
                        application.setDeploymentTime(Long.toString(timeTakenToDeploy));
                        application.setTimeDeployed(Long.toString(deploymentTimeMillis));

                        // register application information in domain.xml
                        deployment.registerAppInDomainXML(appInfo, deploymentContext, tx);

                    }
                    if (retrieve != null) {
                        retrieveArtifacts(context, downloadableArtifacts.getArtifacts(), retrieve, false, name);
                    }
                    suppInfo.setDeploymentContext(deploymentContext);
                    // send new event to notify the deployment process is finish
                    events.send(new Event(Deployment.DEPLOYMENT_COMMAND_FINISH, appInfo), false);

                    //Fix for issue 14442
                    //We want to report the worst subreport value.
                    ActionReport.ExitCode worstExitCode = ExitCode.SUCCESS;
                    for (ActionReport subReport : report.getSubActionsReport()) {
                        ActionReport.ExitCode actionExitCode = subReport.getActionExitCode();

                        if (actionExitCode.isWorse(worstExitCode)) {
                            worstExitCode = actionExitCode;
                        }
                    }
                    report.setActionExitCode(worstExitCode);
                    report.setResultType(String.class, name);

                } catch (Exception e) {
                    // roll back the deployment and re-throw the exception
                    deployment.undeploy(name, deploymentContext);
                    deploymentContext.clean();
                    throw e;
                }
            }
        } catch (Throwable e) {
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setFailureCause(e);
            if(e.getMessage() != null) {
                report.setMessage(e.getMessage());
            }
        } finally {
            events.unregister(this);
            try {
                archive.close();
            } catch (IOException e) {
                logger.log(Level.FINE, localStrings.getLocalString(
                        "errClosingArtifact",
                        "Error while closing deployable artifact : ",
                        path.getAbsolutePath()), e);
            }

            if (structuredTracing.isEnabled()) {
                structuredTracing.print(System.out);
            }

            if (report.getActionExitCode().equals(ActionReport.ExitCode.SUCCESS)) {
                // Set the app name in the result so that embedded deployer can retrieve it.
                report.setResultType(String.class, name);
                report.setMessage(localStrings.getLocalString("deploy.command.success", "Application deployed with name {0}", name));

                logger.info(localStrings.getLocalString(
                        "deploy.done",
                        "Deployment of {0} done is {1} ms at {2}",
                        name,
                        timeTakenToDeploy, DateFormat.getDateInstance().format(new Date(deploymentTimeMillis))));
            } else if (report.getActionExitCode().equals(ActionReport.ExitCode.FAILURE)) {
                String errorMessage = report.getMessage();
                Throwable cause = report.getFailureCause();
                if (cause != null) {
                    String causeMessage = cause.getMessage();
                    if (causeMessage != null
                            && !causeMessage.equals(errorMessage)) {
                        errorMessage = errorMessage + " : " + cause.getMessage();
                    }
                    logger.log(Level.SEVERE, errorMessage, cause.getCause());
                }
                report.setMessage(localStrings.getLocalString("deploy.errDuringDepl", "Error occur during deployment: {0}.", errorMessage));
                // reset the failure cause so command framework will not try
                // to print the same message again
                report.setFailureCause(null);
                if (expansionDir != null) {
                    final FileArchive arch;
                    try {
                        /*
                         * Open and then delete the expansion directory as
                         * a file archive so stale file handling can run.
                         */
                        arch = DeploymentUtils.openAsFileArchive(expansionDir, archiveFactory);
                        arch.delete();
                    } catch (IOException ex) {
                        final String msg = localStrings.getLocalString(
                                "deploy.errDelRepos",
                                "Error deleting repository directory {0}",
                                expansionDir.getAbsolutePath());
                        report.failure(logger, msg, ex);
                    }

                }
                appState.map(ApplicationState::getPath)
                        .ifPresent(hotDeployService::removeApplicationState);
            }
            if (deploymentContext != null && !loadOnly) {
                deploymentContext.postDeployClean(true);
            }
            appState.ifPresent(ApplicationState::close);
        }
    }

    /**
     * Makes safe copies of the archive, deployment plan, alternate dd, runtime
     * alternate dd for later use during instance sync activity.
     * 

* We rename any uploaded files from the temp directory to the permanent * place, and we copy any archive files that were not uploaded. This * prevents any confusion that could result if the developer modified the * archive file - changing its lastModified value - before redeploying it. * * @param deploymentContext * @param logger logger * @throws IOException */ private void moveAppFilesToPermanentLocation( final ExtendedDeploymentContext deploymentContext, final Logger logger) throws IOException { final File finalUploadDir = deploymentContext.getAppInternalDir(); final File finalAltDDDir = deploymentContext.getAppAltDDDir(); if (!finalUploadDir.mkdirs()) { logger.log(Level.FINE, " Attempting to create upload directory {0} was reported as failed; attempting to continue", new Object[]{finalUploadDir.getAbsolutePath()}); } // PAYAYRA-444 GLASSFISH-21371 if (!finalAltDDDir.mkdirs()) { logger.log(Level.FINE, " Attempting to create altdd directory {0} was reported as failed; attempting to continue", new Object[]{finalAltDDDir.getAbsolutePath()}); } safeCopyOfApp = DeploymentCommandUtils.renameUploadedFileOrCopyInPlaceFile(finalUploadDir, originalPathValue, logger, env); safeCopyOfDeploymentPlan = DeploymentCommandUtils.renameUploadedFileOrCopyInPlaceFile(finalUploadDir, deploymentplan, logger, env); safeCopyOfAltDD = DeploymentCommandUtils.renameUploadedFileOrCopyInPlaceFile(finalAltDDDir, altdd, logger, env); safeCopyOfRuntimeAltDD = DeploymentCommandUtils.renameUploadedFileOrCopyInPlaceFile(finalAltDDDir, runtimealtdd, logger, env); } private void recordFileLocations( final Properties appProps) throws URISyntaxException { /* * Setting the properties in the appProps now will cause them to be * stored in the domain.xml elements along with other properties when * the entire config is saved. */ if (safeCopyOfApp != null) { appProps.setProperty(Application.APP_LOCATION_PROP_NAME, DeploymentUtils.relativizeWithinDomainIfPossible( safeCopyOfApp.toURI())); } if (safeCopyOfDeploymentPlan != null) { appProps.setProperty(Application.DEPLOYMENT_PLAN_LOCATION_PROP_NAME, DeploymentUtils.relativizeWithinDomainIfPossible( safeCopyOfDeploymentPlan.toURI())); } if (safeCopyOfAltDD != null) { appProps.setProperty(Application.ALT_DD_LOCATION_PROP_NAME, DeploymentUtils.relativizeWithinDomainIfPossible( safeCopyOfAltDD.toURI())); } if (safeCopyOfRuntimeAltDD != null) { appProps.setProperty( Application.RUNTIME_ALT_DD_LOCATION_PROP_NAME, DeploymentUtils.relativizeWithinDomainIfPossible( safeCopyOfRuntimeAltDD.toURI())); } } /** * Check if the application is deployed or not. If force option is true and * appInfo is not null, then undeploy the application and return false. This * will force deployment if there's already a running application deployed. * * @param name application name * @param report ActionReport, report object to send back to client. * @return context properties that might have been set by the deployers * while undeploying the application * */ private Properties handleRedeploy(final String name, final ActionReport report, final AdminCommandContext context) throws Exception { if (isredeploy) { //preserve settings first before undeploy Application app = apps.getModule(Application.class, name); if (app.isLifecycleModule()) { throw new IllegalArgumentException(localStrings.getLocalString("lifecyclemodule_withsamename_exists", "Lifecycle module with same name {0} already exists, please pick a different name for the application. ", name)); } // we save some of the old registration information in our deployment parameters settingsFromDomainXML(app); //if application is already deployed and force=true, //then undeploy the application first. // Use ParameterMap till we have a better way // to invoke a command on both DAS and instance with the // replication framework final ParameterMap parameters = new ParameterMap(); parameters.add("DEFAULT", name); parameters.add(DeploymentProperties.TARGET, target); parameters.add(DeploymentProperties.KEEP_REPOSITORY_DIRECTORY, keepreposdir.toString()); parameters.add(DeploymentProperties.IS_REDEPLOY, isredeploy.toString()); if (dropandcreatetables != null) { parameters.add(DeploymentProperties.DROP_TABLES, dropandcreatetables.toString()); } parameters.add(DeploymentProperties.IGNORE_CASCADE, force.toString()); if (keepstate != null) { parameters.add(DeploymentProperties.KEEP_STATE, keepstate.toString()); } ActionReport subReport = report.addSubActionsReport(); subReport.setExtraProperties(new Properties()); List propertyNames = new ArrayList(); propertyNames.add(DeploymentProperties.KEEP_SESSIONS); propertyNames.add(DeploymentProperties.PRESERVE_APP_SCOPED_RESOURCES); populatePropertiesToParameterMap(parameters, propertyNames); CommandRunner.CommandInvocation inv = commandRunner.getCommandInvocation("undeploy", subReport, context.getSubject()); inv.parameters(parameters).execute(); return subReport.getExtraProperties(); } return null; } private void populatePropertiesToParameterMap(ParameterMap parameters, List propertyNamesList) { Properties props = new Properties(); if (properties != null) { for (String propertyName : propertyNamesList) { if (properties.containsKey(propertyName)) { props.put(propertyName, properties.getProperty(propertyName)); } } } parameters.add("properties", DeploymentUtils.propertiesValue(props, ':')); } /** * Places into the outgoing payload the downloadable artifacts for an * application. * * @param context the admin command context for the command requesting the * artifacts download * @param app the application of interest * @param targetLocalDir the client-specified local directory to receive the * downloaded files */ public static void retrieveArtifacts(final AdminCommandContext context, final Application app, final String targetLocalDir) { retrieveArtifacts(context, app, targetLocalDir, true); } /** * Places into the outgoing payload the downloadable artifacts for an * application. * * @param context the admin command context for the command currently * running * @param app the application of interest * @param targetLocalDir the client-specified local directory to receive the * downloaded files * @param reportErrorsInTopReport whether to include error indications in * the report's top-level */ public static void retrieveArtifacts(final AdminCommandContext context, final Application app, final String targetLocalDir, final boolean reportErrorsInTopReport) { retrieveArtifacts(context, DeploymentUtils.downloadableArtifacts(app).getArtifacts(), targetLocalDir, reportErrorsInTopReport, app.getName()); } private static void retrieveArtifacts(final AdminCommandContext context, final Collection artifactInfo, final String targetLocalDir, final boolean reportErrorsInTopReport, final String appname) { if (artifactInfo.isEmpty()) { final ActionReport report = context.getActionReport(); final ActionReport subReport = report.addSubActionsReport(); subReport.setMessage(localStrings.getLocalString( DeployCommand.class, "get-client-stubs.noStubApp", "There are no files to retrieve for application {0}", new Object[]{appname})); subReport.setActionExitCode(ExitCode.SUCCESS); return; } Logger logger = context.getLogger(); FileOutputStream targetStream = null; try { Payload.Outbound outboundPayload = context.getOutboundPayload(); // GLASSFISH-17554: pass to DownloadServlet boolean retrieveArtifacts = false; if (outboundPayload == null) { outboundPayload = PayloadImpl.Outbound.newInstance(); retrieveArtifacts = true; } Properties props = new Properties(); /* * file-xfer-root is used as a URI, so convert backslashes. */ props.setProperty("file-xfer-root", targetLocalDir.replace('\\', '/')); for (Artifacts.FullAndPartURIs uriPair : artifactInfo) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "About to download artifact {0}", uriPair.getFull()); } outboundPayload.attachFile("application/octet-stream", uriPair.getPart(), "files", props, new File(uriPair.getFull().getSchemeSpecificPart())); } if (retrieveArtifacts) { File targetLocalFile = new File(targetLocalDir); // CAUTION: file instead of dir if (targetLocalFile.exists()) { final String msg = localStrings.getLocalString("download.errFileExists", "Unable to generate files. File [{0}] already exists.", targetLocalFile.getAbsolutePath()); throw new Exception(msg); } if (!targetLocalFile.getParentFile().exists()) { final String msg = localStrings.getLocalString("download.errParentFileMissing", "Unable to generate files. Directory [{0}] does not exist.", targetLocalFile.getParent()); throw new Exception(msg); } targetStream = new FileOutputStream(targetLocalFile); outboundPayload.writeTo(targetStream); targetStream.flush(); } } catch (Exception e) { handleRetrieveException(e, context, reportErrorsInTopReport); } finally { if (targetStream != null) { try { targetStream.close(); } catch (IOException ex) { handleRetrieveException(ex, context, reportErrorsInTopReport); } } } } private static void handleRetrieveException(final Exception e, final AdminCommandContext context, final boolean reportErrorsInTopReport) { final String errorMsg = localStrings.getLocalString( "download.errDownloading", "Error while downloading generated files"); final Logger logger = context.getLogger(); logger.log(Level.SEVERE, errorMsg, e); ActionReport report = context.getActionReport(); if (!reportErrorsInTopReport) { report = report.addSubActionsReport(); report.setActionExitCode(ExitCode.WARNING); } else { report.setActionExitCode(ExitCode.FAILURE); } report.setMessage(errorMsg); report.setFailureCause(e); } /** * Get settings from domain.xml and preserve the values. This is a private * api and its invoked when --force=true and if the app is registered. * * @param app is the registration information about the previously deployed * application * */ private void settingsFromDomainXML(Application app) { //if name is null then cannot get the application's setting from domain.xml if (name != null) { if (contextroot == null) { if (app.getContextRoot() != null) { this.previousContextRoot = app.getContextRoot(); } } if (libraries == null) { libraries = app.getLibraries(); } previousTargets = domain.getAllReferencedTargetsForApplication(name); if (virtualservers == null) { if (DeploymentUtils.isDomainTarget(target)) { for (String tgt : previousTargets) { String vs = domain.getVirtualServersForApplication(tgt, name); if (vs != null) { previousVirtualServers.put(tgt, vs); } } } else { virtualservers = domain.getVirtualServersForApplication( target, name); } } if (enabled == null) { if (DeploymentUtils.isDomainTarget(target)) { // save the enable attributes of the application-ref for (String tgt : previousTargets) { previousEnabledAttributes.put(tgt, domain.getEnabledForApplication(tgt, name)); } // save the enable attribute of the application previousEnabledAttributes.put(DeploymentUtils.DOMAIN_TARGET_NAME, app.getEnabled()); // set the enable command param for DAS enabled = deployment.isAppEnabled(app); } else { enabled = Boolean.valueOf(domain.getEnabledForApplication( target, name)); } } String compatProp = app.getDeployProperties().getProperty( DeploymentProperties.COMPATIBILITY); if (compatProp != null) { if (properties == null) { properties = new Properties(); } // if user does not specify the compatibility flag // explictly in this deployment, set it to the old value if (properties.getProperty(DeploymentProperties.COMPATIBILITY) == null) { properties.setProperty(DeploymentProperties.COMPATIBILITY, compatProp); } } } } @Override public void event(Event event) { if (event.is(Deployment.DEPLOYMENT_BEFORE_CLASSLOADER_CREATION)) { // this is where we have processed metadata and // haven't created the application classloader yet DeploymentContext context = (DeploymentContext) event.hook(); if (verify) { Verifier verifier = habitat.getService(Verifier.class); if (verifier != null) { verifier.verify(context); } else { context.getLogger().warning("Verifier is not installed yet. Install verifier module."); } } } } private void validateDeploymentProperties(Properties properties, DeploymentContext context) { String compatProp = properties.getProperty( DeploymentProperties.COMPATIBILITY); if (compatProp != null && !compatProp.equals("v2")) { // this only allowed value for property compatibility is v2 String warningMsg = localStrings.getLocalString("compat.value.not.supported", "{0} is not a supported value for compatibility property.", compatProp); ActionReport subReport = context.getActionReport().addSubActionsReport(); subReport.setActionExitCode(ActionReport.ExitCode.WARNING); subReport.setMessage(warningMsg); context.getLogger().log(Level.WARNING, warningMsg); } } /** * Crude interception mechanisms for deploy comamnd execution */ @Contract public interface Interceptor { /** * Called by the deployment command to intercept a deployment activity. * * @param self the deployment command in flight. * @param context of the deployment */ void intercept(DeployCommand self, DeploymentContext context); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy