Package com.salesforce.ide.deployment.ui.wizards

Source Code of com.salesforce.ide.deployment.ui.wizards.DeploymentController

/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.deployment.ui.wizards;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

import com.salesforce.ide.api.metadata.types.Package;
import com.salesforce.ide.api.metadata.types.PackageTypeMembers;
import com.salesforce.ide.core.factories.FactoryException;
import com.salesforce.ide.core.factories.PackageManifestFactory;
import com.salesforce.ide.core.internal.context.ContainerDelegate;
import com.salesforce.ide.core.internal.controller.Controller;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.ForceExceptionUtils;
import com.salesforce.ide.core.internal.utils.LoggingInfo;
import com.salesforce.ide.core.internal.utils.Messages;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.ComponentList;
import com.salesforce.ide.core.model.ProjectPackage;
import com.salesforce.ide.core.model.ProjectPackageList;
import com.salesforce.ide.core.project.ForceProject;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.Connection;
import com.salesforce.ide.core.remote.ForceConnectionException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.core.remote.InsufficientPermissionsException;
import com.salesforce.ide.core.remote.metadata.CustomObjectNameResolver;
import com.salesforce.ide.core.remote.metadata.DeployMessageExt;
import com.salesforce.ide.core.remote.metadata.DeployResultExt;
import com.salesforce.ide.core.remote.metadata.RetrieveResultExt;
import com.salesforce.ide.core.services.DeployException;
import com.salesforce.ide.core.services.PackageDeployService;
import com.salesforce.ide.core.services.RetrieveException;
import com.salesforce.ide.core.services.ServiceException;
import com.salesforce.ide.core.services.ServiceLocator;
import com.salesforce.ide.core.services.ServiceTimeoutException;
import com.salesforce.ide.deployment.ForceIdeDeploymentPlugin;
import com.salesforce.ide.deployment.PackageManifest;
import com.salesforce.ide.deployment.internal.DeploymentComponent;
import com.salesforce.ide.deployment.internal.DeploymentComponentSet;
import com.salesforce.ide.deployment.internal.DeploymentPayload;
import com.salesforce.ide.deployment.internal.DeploymentResult;
import com.salesforce.ide.deployment.internal.DeploymentSummary;
import com.salesforce.ide.deployment.internal.utils.DeploymentConstants;
import com.salesforce.ide.ui.internal.utils.ComponentArchiver;
import com.sforce.soap.metadata.DeployOptions;
import com.sforce.soap.metadata.LogInfo;
import com.sforce.ws.ConnectionException;

public class DeploymentController extends Controller {
    private static final Logger logger = Logger.getLogger(DeploymentController.class);

    private DeploymentPayload deploymentPayload = null;
    private DeploymentResult deploymentResult = null;
    private ComponentList remoteComponentList = null;
    private ProjectPackageList localProjectPackageList = null;
    private ProjectPackageList remoteProjectPackageList = null;

    public DeploymentController(IProject project) throws ForceProjectException {
        super();
        model = new DeploymentWizardModel(project);
    }

    public DeploymentController(IProject project, List<IResource> resources) throws ForceProjectException {
        super();
        model = new DeploymentWizardModel(project, resources);
    }

    @Override
    public void init() throws ForceProjectException {

    }

    public DeploymentWizardModel getDeploymentWizardModel() {
        return (DeploymentWizardModel) model;
    }

    public void setDeploymentWizardModel(DeploymentWizardModel deploymentWizardModel) {
        this.model = deploymentWizardModel;
    }

    public ProjectPackageList getRemoteProjectPackageList() {
        return remoteProjectPackageList;
    }

    public DeploymentPayload getDeploymentPayload() {
        return deploymentPayload;
    }

    public void setDeploymentPayload(DeploymentPayload deploymentPayload) {
        this.deploymentPayload = deploymentPayload;
    }

    public DeploymentResult getDeploymentResult() {
        return deploymentResult;
    }

    public void setDeploymentResult(DeploymentResult deploymentResult) {
        this.deploymentResult = deploymentResult;
    }

    public boolean isDeploymentPayloadEmpty() {
        return deploymentPayload == null || deploymentPayload.isEmpty();
    }

    public boolean isSameAsProjectOrg(String username, String endpoint) {
        if (Utils.isEmpty(username) || Utils.isEmpty(endpoint)) {
            return false;
        }

        ForceProject forceProject = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getForceProject(getProject());
        if (username.equals(forceProject.getUserName()) && endpoint.equals(forceProject.getEndpointServer())) {
            return true;
        }

        return false;
    }

    @Override
    public void dispose() {
        model = null;
    }

    public void testDeploy(IProgressMonitor monitor) throws ForceConnectionException, DeployException,
            FactoryException, Exception, RemoteException, InterruptedException {
        deploymentResult = deploy(true, monitor);
    }

    private DeploymentResult deploy(boolean checkOnly, IProgressMonitor monitor) throws ForceConnectionException,
            DeployException, FactoryException, Exception, RemoteException, InterruptedException {
        return deploy(getDeploymentWizardModel().getDestinationOrg(), deploymentPayload, checkOnly, monitor);
    }

    public void generateArchive(DeploymentComponentSet deploymentComponents, File zipPath, String zipName)
            throws IOException, FactoryException {
        // should already have composite components in deployment set
        ComponentList components = deploymentComponents.getComponents(false);
        generateArchive(components, zipPath, zipName);
    }

    public void generateArchive(ComponentList components, File zipPath, String zipName) throws IOException {
        ComponentArchiver.generateArchive(zipName, zipPath, components);
        if (logger.isDebugEnabled()) {
            logger.debug("Generated archive: " + zipPath + File.separator + zipName);
        }
    }

    public void testConnection() throws ForceConnectionException, InsufficientPermissionsException {
        if (getDeploymentWizardModel().getDestinationOrg() != null) {
            ContainerDelegate.getInstance().getFactoryLocator().getConnectionFactory().getConnection(getDeploymentWizardModel().getDestinationOrg());
        }
    }

    public void saveSettings(IProgressMonitor monitor) throws InterruptedException {
        saveArchiveSettings(monitor);
        saveOrgSettings(monitor);
    }

    public void saveArchiveSettings(IProgressMonitor monitor) throws InterruptedException {
        if (getDeploymentWizardModel() == null || getDeploymentWizardModel().getForceProject() == null) {
            throw new IllegalArgumentException("Project model and/or project cannot be null");
        }

        monitorCheck(monitor);

        if (logger.isDebugEnabled()) {
            logger.debug("Saving deployment archive '" + getDeploymentWizardModel().getProjectName() + "' settings");
        }

        ForceProject forceProject = getDeploymentWizardModel().getForceProject();

        if (forceProject == null) {
            logger.warn("Unable to save last entered/select deployment details - force project is null");
            return;
        }

        // save workspace settings
        if (getDeploymentWizardModel().getDestinationArchivePath() != null) {
            ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_SOURCE_DEPLOYMENT_ARCHIVE_DIR_SELECTED,
                getDeploymentWizardModel().getDestinationArchivePath().toString());
        }

        if (getDeploymentWizardModel().getDestinationArchivePath() != null) {
            ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEST_DEPLOYMENT_ARCHIVE_DIR_SELECTED,
                getDeploymentWizardModel().getDestinationArchivePath().toString());
        }
    }

    public void saveOrgSettings(IProgressMonitor monitor) throws InterruptedException {
        if (getDeploymentWizardModel() == null || getDeploymentWizardModel().getForceProject() == null) {
            throw new IllegalArgumentException("Deployment model and/or force project cannot be null");
        }

        monitorCheck(monitor);

        if (logger.isDebugEnabled()) {
            logger.debug("Saving destination org '" + getDeploymentWizardModel().getProjectName() + "' settings");
        }

        ForceProject forceProject = getDeploymentWizardModel().getForceProject();

        if (forceProject == null) {
            logger.warn("Unable to save last entered/select deployment details - force project is null");
            return;
        }

        ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEPLOYMENT_ENV_SELECTED,
            getDeploymentWizardModel().getEnvironment());

        ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEPLOYMENT_USERNAME_SELECTED, forceProject
                .getUserName());

        String otherLabel = Messages.getString("ProjectCreateWizard.OrganizationPage.OtherEnvironment.label");
        if (otherLabel.equals(getDeploymentWizardModel().getEnvironment())) {
            ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEPLOYMENT_SERVER_SELECTED, forceProject
                    .getEndpointServer());
            ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEPLOYMENT_KEEP_ENDPOINT_SELECTED,
                forceProject.isKeepEndpoint());
            ForceIdeDeploymentPlugin.savePreference(DeploymentConstants.LAST_DEPLOYMENT_PROTOCOL_SELECTED, forceProject
                    .isHttpsProtocol());
        }
    }

    public void generateDeploymentPayload(IProgressMonitor monitor) throws Exception {
        if (getDeploymentWizardModel() == null || getDeploymentWizardModel().getDestinationOrg() == null) {
            throw new IllegalArgumentException("Project and/or destination connection are not provided.");
        }

        monitorWorkCheck(monitor, "Getting connection...");
        ForceProject destinationProject = getDeploymentWizardModel().getDestinationOrg();
        try {
            monitorWorkCheck(monitor, "Generating deployment payload...");
            DeploymentPayload deploymentPayload =
                    generateDeploymentPayload(getDeploymentWizardModel().getDeployResources(), destinationProject,
                        new SubProgressMonitor(monitor, 4));
            setDeploymentPayload(deploymentPayload);
        } finally {
            if (monitor != null) {
                monitor.done();
            }
        }
    }

    public DeploymentPayload generateDeploymentPayload(IResource deployResource, ForceProject destinationProject,
            IProgressMonitor monitor) throws InterruptedException, ForceConnectionException, ForceRemoteException,
            CoreException, FactoryException, RetrieveException, IOException, ServiceTimeoutException {
        List<IResource> deployResources = new ArrayList<IResource>(1);
        deployResources.add(deployResource);
        return generateDeploymentPayload(deployResources, destinationProject, monitor);
    }

    /**
     * Inspects project contents and given remote destination org to determine a deployment plan containing deployment
     * candidates and their respective deployment action - new, overwrite, delete, etc.
     *
     * @param project
     * @param destinationProject
     * @return
     * @throws InterruptedException
     * @throws ConnectionException
     * @throws RemoteException
     * @throws Exception
     * @throws FactoryException
     * @throws CoreException
     * @throws RetrieveException
     * @throws IOException
     * @throws ServiceTimeoutException
     * @throws MetadataServiceException
     */
    // FIXME: review exception throwing
    public DeploymentPayload generateDeploymentPayload(List<IResource> deployResources,
            ForceProject destinationProject, IProgressMonitor monitor) throws InterruptedException,
            ForceConnectionException, ForceRemoteException, CoreException, FactoryException, RetrieveException,
            IOException, ServiceTimeoutException {
        if (Utils.isEmpty(deployResources) || destinationProject == null || model.getProject() == null) {
            throw new IllegalArgumentException("Resources, project, and/or destination cannot be null");
        }

        if (logger.isInfoEnabled()) {
            logger.info("Generating deployment plan for " + destinationProject.getLogDisplay());
        }

        // initialize payload container
        DeploymentPayload deploymentPayload = new DeploymentPayload(model.getProject(), deployResources);
        deploymentPayload.setDestinationOrgUsername(destinationProject.getUserName());

        monitorCheckSubTask(monitor, "Getting connection to destination organization...");
        Connection connection = ContainerDelegate.getInstance().getFactoryLocator().getConnectionFactory().getConnection(destinationProject);
        monitorWork(monitor);

        monitorCheckSubTask(monitor, "Getting permissible object types for destination organization...");
        String[] enabledComponentTypes = ContainerDelegate.getInstance().getServiceLocator().getMetadataService().getEnabledComponentTypes(connection, true, true);
        if (Utils.isEmpty(enabledComponentTypes)) {
            logger.warn("No object types are enabled for " + connection.getLogDisplay());
            return deploymentPayload;
        }
        monitorWork(monitor);

        List<String> remoteEnabledComponentTypes = new ArrayList<String>(enabledComponentTypes.length);
        remoteEnabledComponentTypes.addAll(Arrays.asList(enabledComponentTypes));

        monitorCheckSubTask(monitor, "Gathering project contents for resource(s)");
        // get local and remote components
        localProjectPackageList = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectContents(deployResources, true, monitor);
        localProjectPackageList.setProject(model.getProject());
        monitorWork(monitor);

        // there's nothing to deploy if local is empty
        if (localProjectPackageList.isEmpty()) {
            logger.warn("Local package list is empty");
            return deploymentPayload;
        }

        //  retrieve remote components
        try {
            loadRemoteProjectPackageList(connection, deployResources, monitor);
        } catch (Exception e) {
            logger.warn("Unable to retrieve remote components for resources", ForceExceptionUtils.getRootCause(e));
        }

        // if remote is empty, are components are considered new
        if (Utils.isEmpty(remoteProjectPackageList)) {
            if (logger.isInfoEnabled()) {
                logger.info("Remote package list is empty.  Assuming all local components are new.");
            }
            ComponentList componentList = localProjectPackageList.getAllComponents();
            for (Component component : componentList) {
                DeploymentComponent deploymentComponent =
                        createNewDeploymentComponent(component, remoteEnabledComponentTypes);
                deploymentComponent.setRemoteFound(false);

                boolean added = deploymentPayload.add(deploymentComponent, true);
                if (added) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Added  " + deploymentComponent.getFullDisplayName() + " to deployment set");
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug(deploymentComponent.getDisplayName() + " already exists in deployment set");
                    }
                }
            }

        } else {
            // evaluate local against remote retrieved
            if (logger.isDebugEnabled()) {
                logger.debug("Local package count [" + localProjectPackageList.size() + "] and component count ["
                        + localProjectPackageList.getComponentCount(true) + "]");
                logger.debug("Remote package count ["
                        + (Utils.isNotEmpty(remoteProjectPackageList) ? remoteProjectPackageList.size() : 0)
                        + "] and component count ["
                        + (Utils.isNotEmpty(remoteProjectPackageList) ? remoteProjectPackageList
                                .getComponentCount(true) : 0) + "]");
            }

            ComponentList remoteComponents = remoteProjectPackageList.getAllComponents();
            if (Utils.isNotEmpty(remoteComponents)) {
                deploymentPayload.setRemoteComponentList(remoteComponents);
            }

            monitorCheckSubTask(monitor, "Evaluating project and remote components...");
            // test either project package
            for (ProjectPackage localProjectPackage : localProjectPackageList) {
                monitorCheck(monitor);
                DeploymentComponentSet deploymentComponentSet =
                        getDeploymentComponentSetForPackage(localProjectPackage, remoteProjectPackageList,
                            remoteEnabledComponentTypes, monitor);
                deploymentPayload.addAll(deploymentComponentSet);
            }
            monitorWork(monitor);

            if (logger.isDebugEnabled()) {
                logger.debug("Deployment load count [" + deploymentPayload.size() + "]");
                logger.debug(deploymentPayload.getDeploymentPlanSummary());
            }
        }

        return deploymentPayload;
    }

    protected void loadRemoteProjectPackageList(Connection connection, List<IResource> deployResources,
            IProgressMonitor monitor) throws ForceConnectionException, ForceRemoteException, InterruptedException,
            FactoryException, CoreException, IOException, InvocationTargetException, ServiceException {
        if (Utils.isEmpty(deployResources)) {
            logger.warn("Unable to get remote content for resources - resources is null or empty");
        }

        if (logger.isDebugEnabled()) {
            StringBuffer strBuffer =
                    new StringBuffer("Retrieve remote components for the following resource roots ["
                            + deployResources.size() + "]:");
            int resourceCnt = 0;
            for (IResource resource : deployResources) {
                strBuffer.append("\n (").append(++resourceCnt).append(") ").append(
                    resource.getProjectRelativePath().toPortableString());
            }
            logger.debug(strBuffer.toString());
        }

        monitorCheck(monitor);
        monitorSubTask(monitor, "Retrieving remote components...");

        remoteProjectPackageList = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectPackageListInstance();
        remoteProjectPackageList.setProject(model.getProject());

        // handle if source root was selected
        try {
            handleSourceRetrieve(connection, deployResources, monitor);

            // handle source component folders
            // handling sub-src folder selections: some are individual file retrieves and some selections should
            // include new content.  this difference requires that we either query all content and pair-down or make
            // multiple specific, retrieve calls.  the latter was chosen so that we don't bog down the server with large
            // requests and the ide from having to parse large result sets.
            handleSourceComponentFolderRetrieve(connection, deployResources, monitor);

            // handle source component files
            handleSourceComponentFileRetrieve(connection, deployResources, monitor);
        } catch (RetrieveException ex) {
            // okay if pkg is not found - in handlePackageRetrieve we inspect if components exists as not packaged
            if (!ForceExceptionUtils.isPackageNotFoundException(ex)) {
                throw ex;
            }
        }

        // for package content, see if unpackaged instance exists
        handlePackageRetrieve(connection, deployResources, monitor);

        if (logger.isDebugEnabled()) {
            logger.debug("Retrieved  "
                    + (Utils.isNotEmpty(remoteProjectPackageList) ? remoteProjectPackageList.size() : 0)
                    + " components");
        }
    }

    protected void handleSourceRetrieve(Connection connection, List<IResource> deployResources, IProgressMonitor monitor)
            throws InterruptedException, ForceConnectionException, ForceRemoteException, FactoryException,
            CoreException, IOException, ServiceException {
        monitorCheck(monitor);
        if (Utils.isNotEmpty(ContainerDelegate.getInstance().getServiceLocator().getProjectService().getFolder(deployResources, Constants.SOURCE_FOLDER_NAME))) {
            String packageName = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getPackageName(model.getProject());

            // perform retrieve
            RetrieveResultExt retrieveResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrievePackage(connection, model.getProject(),
                        packageName, monitor);

            if (retrieveResultHandler == null) {
                logger.warn("Unable to retrieve remote components for src folder - retrieve result is null");
                return;
            }

            monitorWork(monitor);

            remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                    .getFileMetadataHandler());
        }
    }

    protected void handleSourceComponentFolderRetrieve(Connection connection, List<IResource> deployResources,
            IProgressMonitor monitor) throws InterruptedException, ForceConnectionException, ForceRemoteException,
            FactoryException, CoreException, IOException, InvocationTargetException, ServiceException {

        // if only source root was selected was selected, let's end here
        if (deployResources.size() == 1
                && Utils.isNotEmpty(ContainerDelegate.getInstance().getServiceLocator().getProjectService().getFolder(deployResources, Constants.SOURCE_FOLDER_NAME))) {
            return;
        }

        monitorCheck(monitor);
        List<IResource> folders = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getResourcesByType(deployResources, IResource.FOLDER);
        List<String> componentTypes = new ArrayList<String>();
        if (Utils.isNotEmpty(folders)) {
            for (IResource folder : folders) {
                if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isComponentFolder(folder)) {
                    Component component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentByFolderName(folder.getName());
                    componentTypes.add(component.getComponentType());
                } else if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isSubComponentFolder(folder)) {
                    Component component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentFromSubFolder((IFolder) folder, false);
                    componentTypes.add(component.getSecondaryComponentType());
                }
            }

            if (Utils.isNotEmpty(componentTypes)) {
                // refresh component dirs
                ProjectPackageList projectPackageList =
                        ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectPackageFactory().getProjectPackageListInstance(model.getProject());

                // only save these types
                String[] componentTypeArray = componentTypes.toArray(new String[componentTypes.size()]);

                // perform retrieve
                RetrieveResultExt retrieveResultHandler =
                        ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrieveSelective(connection, projectPackageList,
                            componentTypeArray, monitor);

                if (retrieveResultHandler == null) {
                    logger
                            .warn("Unable to retrieve remote components for component folder(s) - retrieve result is null");
                    return;
                }

                monitorWork(monitor);

                remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                        .getFileMetadataHandler());
            }
        }
    }

    // we retrieve using the projects package.xml which designates which package the api will inspect and
    // retrieve from.  if a component of the same name exists in the remote org, but is not in the project's
    // package, we need to mark that the component will be overwritten.  note that a component can be in multiple
    // packages and that packages are basically just tags on components, not necessary self-contained units.
    // for example, assuming it is really <nonamespace>.myApexClass & <nonamespace>.myApexClass is already
    // on the server, it will update <nonamespace>.myApexClass AND add <nonamespace>.myApexClass to the
    // newly created 'mypackage' (it's quite possible <nonamespace>.myApexClass was already a member of
    // another package; that relationship would be unchanged).
    protected void handlePackageRetrieve(Connection connection, List<IResource> deployResources,
            IProgressMonitor monitor) throws InterruptedException, ForceConnectionException, ForceRemoteException,
            FactoryException, CoreException, IOException, InvocationTargetException, ServiceException {

        // 1.) get list of component not found in remote org - assume components don't exist in package, but might be unpackaged
        // 2.) selectively query for said components
        // 3.) added return components to remoteProjectPackageList

        String packageName = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getPackageName(getProject());
        // we're only interested if the project's is package content
        if (Utils.isEmpty(packageName) || Constants.DEFAULT_PACKAGED_NAME.equals(packageName)) {
            return;
        }

        monitorCheck(monitor);
        ComponentList componentList = localProjectPackageList.getComponentsNotFound(remoteProjectPackageList);

        // all components present in remote org
        if (!Utils.isEmpty(componentList)) {
            // check unpackage content for not-found components
            List<String> componentNames = componentList.getFilePaths(true, false);

            if (logger.isDebugEnabled()) {
                StringBuffer strBuff = new StringBuffer("The following components were not found in package '");
                strBuff.append(packageName).append("', checking if instances exists in unpackaged:");
                int cnt = 0;
                for (String componentName : componentNames) {
                    strBuff.append("  (").append(++cnt).append(" ").append(componentName);
                }
                logger.debug(strBuff.toString());
            }

            RetrieveResultExt retrieveResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrieveSelective(connection,
                        componentNames.toArray(new String[componentNames.size()]), Constants.DEFAULT_PACKAGED_NAME,
                        getProject(), monitor);

            if (retrieveResultHandler == null) {
                logger.warn("Unable to retrieve remote components for component file(s) - retrieve result is null");
                return;
            }

            monitorWork(monitor);

            remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                    .getFileMetadataHandler());
        }
    }

    protected void handleSourceComponentFileRetrieve(Connection connection, List<IResource> deployResources,
            IProgressMonitor monitor) throws InterruptedException, ForceConnectionException, ForceRemoteException,
            FactoryException, CoreException, IOException, InvocationTargetException, ServiceException {

        // if only source root was selected was selected, let's end here
        if (deployResources.size() == 1
                && Utils.isNotEmpty(ContainerDelegate.getInstance().getServiceLocator().getProjectService().getFolder(deployResources, Constants.SOURCE_FOLDER_NAME))) {
            return;
        }

        monitorCheck(monitor);
        List<IResource> files = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getResourcesByType(deployResources, IResource.FILE);
        if (Utils.isNotEmpty(files)) {
            monitorCheck(monitor);
            RetrieveResultExt retrieveResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrieveSelective(connection, localProjectPackageList,
                        true, monitor);

            if (retrieveResultHandler == null) {
                logger.warn("Unable to retrieve remote components for component file(s) - retrieve result is null");
                return;
            }

            monitorWork(monitor);

            remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                    .getFileMetadataHandler());
        }
    }

    // inspect local vs. remote project package content
    private DeploymentComponentSet getDeploymentComponentSetForPackage(ProjectPackage localProjectPackage,
            ProjectPackageList remoteProjectPackageList, List<String> remoteEnabledComponentTypes,
            IProgressMonitor monitor) throws InterruptedException {
        DeploymentComponentSet deploymentComponentSet = new DeploymentComponentSet();

        // local package does not exist remotely, assume all local components are new
        ProjectPackage remoteProjectPackage =
                remoteProjectPackageList.getProjectPackage(localProjectPackage.getName(), false);
        ComponentList localComponentList = localProjectPackage.getComponentList();

        // no corresponding package found in destination org
        if (remoteProjectPackage == null) {
            if (logger.isInfoEnabled()) {
                logger.info("Remote package '" + localProjectPackage.getName()
                        + "' not found.  Assuming all local components [" + localComponentList.size()
                        + "] in package '" + localProjectPackage.getName() + "' are new.");
            }

            // create deployment candidate for each local component
            for (Component localComponent : localComponentList) {

                DeploymentComponent deploymentComponent =
                        createNewDeploymentComponent(localComponent, remoteEnabledComponentTypes);
                deploymentComponent.setRemoteFound(false);

                // if packaged content, check against retrieved non-package stuff to determine if the component
                // exists in the destination, but is not packaged.
                if (!localComponent.isPackageManifest() && Utils.isNotEmpty(localProjectPackage.getName())
                        && !Constants.DEFAULT_PACKAGED_NAME.equals(localProjectPackage.getName())) {
                    Component remoteComponent =
                            remoteProjectPackageList.getComponentByFilePath(localComponent.getMetadataFilePath());

                    if (remoteComponent != null) {
                        initDeploymentComponent(deploymentComponent, localComponent, remoteComponent, monitor);
                        deploymentComponent.setRemoteFound(true);
                    }
                }

                boolean added = deploymentComponentSet.add(deploymentComponent, false);
                if (added) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Added  " + deploymentComponent.getFullDisplayName() + " to deployment set");
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug(deploymentComponent.getDisplayName() + " already exists in deployment set");
                    }
                }
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Added  [" + deploymentComponentSet.size() + "] deployment components to set");
            }

        } else {
            // found package in destination org
            // deep equal check on same-named project package
            remoteComponentList = localProjectPackage.getComponentListInstance();
            remoteComponentList.addAll(remoteProjectPackage.getComponentList());

            if (logger.isDebugEnabled()) {
                logger.debug("Comparing all components in local [" + localComponentList.size() + "] and remote ["
                        + remoteComponentList.size() + "] package '" + localProjectPackage.getName() + "'");
            }

            for (Component localComponent : localComponentList) {
                DeploymentComponent deploymentComponent =
                        getDeploymentComponentForComponent(localComponent, remoteEnabledComponentTypes, monitor);

                boolean added = deploymentComponentSet.add(deploymentComponent, true);
                if (added) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Added  " + deploymentComponent.getFullDisplayName() + " ("
                                + deploymentComponent.getDestinationSummary() + ") to deployment set");
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug(deploymentComponent.getFullDisplayName() + " already exists in deployment set");
                    }
                }
                monitorCheck(monitor);
            }

            // remaining remote components are considered action-able deletes, but are deploy=false (default)
            if (Utils.isNotEmpty(remoteComponentList)) {
                for (Component remoteComponent : remoteComponentList) {
                    DeploymentComponent deploymentComponent = createDeleteDeploymentComponent(remoteComponent);
                    deploymentComponentSet.add(deploymentComponent, false);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Remote component " + remoteComponent.getFullDisplayName()
                                + " does not exist locally and will be consider as a delete candidate");
                    }
                }
            }
        }

        return deploymentComponentSet;
    }

    // evaluate local component vs. remote component
    private DeploymentComponent getDeploymentComponentForComponent(Component localComponent,
            List<String> remoteEnabledComponentTypes, IProgressMonitor monitor) throws InterruptedException {
        // create instance containing local deploy candidate
        DeploymentComponent deploymentComponent =
                createNewDeploymentComponent(localComponent, remoteEnabledComponentTypes);

        Component remoteComponent = remoteComponentList.getComponentByFilePath(localComponent.getMetadataFilePath());
        initDeploymentComponent(deploymentComponent, localComponent, remoteComponent, monitor);

        return deploymentComponent;
    }

    private void initDeploymentComponent(DeploymentComponent deploymentComponent, Component localComponent,
            Component remoteComponent, IProgressMonitor monitor) throws InterruptedException {
        if (deploymentComponent == null) {
            return;
        }

        // if not found in remote list, assume new
        if (remoteComponent == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Did not find " + localComponent.getFullDisplayName() + " in remote package "
                        + localComponent.getPackageName() + ".  Assuming component is new.");
            }

            deploymentComponent.setRemoteFound(false);
        } else if (DeploymentSummary.isDeployable(deploymentComponent.getDestinationSummary())) {
            // if found, compare attributes to identify change
            boolean changed = localComponent.hasEitherChanged(remoteComponent, monitor);
            if (changed) {
                deploymentComponent.setDestinationSummary(DeploymentSummary.UPDATED);
                if (logger.isDebugEnabled()) {
                    logger.debug("Remote component " + remoteComponent.getFullDisplayName() + " has been updated.");
                }
            } else {
                deploymentComponent.setDestinationSummary(DeploymentSummary.NO_CHANGE_OVERWRITE);
                deploymentComponent.setDeploy(false);
                if (logger.isDebugEnabled()) {
                    logger.debug("Local and remote component " + localComponent.getFullDisplayName() + " are equal");
                }
            }

            // removed from further consideration
            // remaining artifacts exist remote only and will be handled later
            if (Utils.isNotEmpty(remoteComponentList)) {
                remoteComponentList.remove(remoteComponent);
            }
        }
    }

    private DeploymentComponent createNewDeploymentComponent(Component component,
            List<String> remoteEnabledComponentTypes) {
        DeploymentComponent deploymentComponent = new DeploymentComponent(component);
        // make sure type is permissible in destination and remote add is supported
        if (Utils.isEmpty(remoteEnabledComponentTypes)
                || !remoteEnabledComponentTypes.contains(component.getComponentType())) {
            deploymentComponent.setDestinationSummary(DeploymentSummary.NOT_PERMISSIBLE);
            deploymentComponent.setDeploy(false);
            if (logger.isDebugEnabled()) {
                logger.debug("Created deployment component for " + component.getFullDisplayName()
                        + ": type not permissible");
            }
        } else if (component.getFileResource() == null || !component.getFileResource().exists()) {
            deploymentComponent.setDestinationSummary(DeploymentSummary.RESOURCE_NOT_FOUND);
            deploymentComponent.setDeploy(false);
            if (logger.isDebugEnabled()) {
                logger.debug("Created deployment component for " + component.getFullDisplayName()
                        + ": resource not found");
            }
        } else if (component.isRemoteAdd()) {
            deploymentComponent.setDestinationSummary(DeploymentSummary.NEW);
            if (logger.isDebugEnabled()) {
                logger.debug("Created deployment component for " + component.getFullDisplayName() + ": new supported");
            }
        } else if (CustomObjectNameResolver.getCheckerForStandardObject().check(component.getName(), component.getComponentType())) {
            deploymentComponent.setDestinationSummary(DeploymentSummary.UPDATED);
            if (logger.isDebugEnabled()) {
                logger.debug("Created deployment component for " + component.getFullDisplayName() + ": updated");
            }
        } else {
            deploymentComponent.setDestinationSummary(DeploymentSummary.NEW_NOT_SUPPORTED);
            deploymentComponent.setDeploy(false);
            if (logger.isDebugEnabled()) {
                logger.debug("Created deployment component for " + component.getFullDisplayName()
                        + ": new not supported");
            }
        }
        return deploymentComponent;
    }

    private DeploymentComponent createDeleteDeploymentComponent(Component component) {
        DeploymentComponent deploymentComponent = new DeploymentComponent(component);
        if (component.isRemoteDeleteable()) {
            deploymentComponent.setDestinationSummary(DeploymentSummary.DELETED);
        } else {
            deploymentComponent.setDestinationSummary(DeploymentSummary.DELETE_NOT_SUPPORTED);
        }
        deploymentComponent.setDeploy(false);
        return deploymentComponent;
    }

    @Override
    public void finish(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
        // record datetime
        Calendar cal = new GregorianCalendar();
        long datetime = cal.getTimeInMillis();

        // create source archive
        monitorCheck(monitor);
        if (Utils.isNotEmpty(getDeploymentWizardModel().getDestinationOrg().getUserName())) {
            monitorSubTask(monitor, "Generating archive of deployed content...");
            File sourceArchivePath = getDeploymentWizardModel().getSourceArchivePath();
            if (sourceArchivePath != null && sourceArchivePath.exists()) {
                try {
                    String archiveName = getDeploymentWizardModel().getProject().getName() + "-deploy-" + datetime;
                    generateArchive(deploymentPayload.getDeploySelectedComponents(true), sourceArchivePath, archiveName);
                } catch (Exception e) {
                    logger.error("Unable to create destination archive.", e);
                }
            }
            monitorWork(monitor);
        }

        // create destination archive
        monitorCheck(monitor);
        File deploymentArchivePath = getDeploymentWizardModel().getDestinationArchivePath();
        if (deploymentArchivePath != null && deploymentArchivePath.exists()) {
            monitorSubTask(monitor, "Generating archive of remote content...");
            try {
                String archiveName =
                        getDeploymentWizardModel().getDestinationOrg().getUserName() + "-deploy-" + datetime;
                generateArchive(deploymentPayload.getRemoteComponentList(), deploymentArchivePath, archiveName);
            } catch (Exception e) {
                logger.error("Unable to create destination archive.", e);
                // REVIEWME: should we continue w/ deployment if we cannot archive the
                // to-be-overwritten components?
            }
            monitorWork(monitor);
        }

        // go for it!
        try {
            deploymentResult = deploy(false, new SubProgressMonitor(monitor, 3));
            monitorWork(monitor);
        } catch (Exception e) {
            throw new InvocationTargetException(e);
        } finally {
            if (logger.isInfoEnabled()) {
                logger.info("Deployment was "
                        + (deploymentResult != null && deploymentResult.isSuccess() ? "SUCCESSFUL" : "FAILED"));
            }
            // save endpoint, if new
            ContainerDelegate.getInstance().getFactoryLocator().getConnectionFactory().getSalesforceEndpoints()
                    .addUserEndpoint(getDeploymentWizardModel().getDestinationOrg().getEndpointServer());
        }
    }

    /**
     * Deploy given components to given destination. Set checkOnly to true if deployment is to be committed.
     *
     * @param destinationProject
     * @param deploymentPayload
     * @param checkOnly
     * @return
     * @throws ForceConnectionException
     * @throws ServiceException
     * @throws FactoryException
     * @throws ForceProjectException
     * @throws InterruptedException
     * @throws ForceRemoteException
     * @throws JAXBException
     */
    public DeploymentResult deploy(ForceProject destinationProject, DeploymentPayload deploymentPayload,
            boolean checkOnly, IProgressMonitor monitor) throws ForceConnectionException, ForceRemoteException,
            InterruptedException, ForceProjectException, FactoryException, ServiceException, JAXBException {
        Connection connection = ContainerDelegate.getInstance().getFactoryLocator().getConnectionFactory().getConnection(destinationProject);
        if (logger.isDebugEnabled()) {
            logger.debug(deploymentPayload.getDeploymentPlanSummary());
        }
        return deployWork(connection, deploymentPayload, checkOnly, monitor);
    }

    private DeploymentResult deployWork(Connection connection, DeploymentPayload deploymentPayload, boolean checkOnly,
            IProgressMonitor monitor) throws InterruptedException, ForceProjectException, FactoryException,
            ForceConnectionException, ServiceException, ForceRemoteException, JAXBException {

        final ServiceLocator serviceLocator = ContainerDelegate.getInstance().getServiceLocator();
        ProjectPackageList projectPackageList = serviceLocator.getProjectService().getProjectPackageListInstance();
        projectPackageList.setProject(deploymentPayload.getProject());
        PackageManifestFactory factory = projectPackageList.getPackageManifestFactory();
        // See W-837427 - we handle custom objects differently upon deploy, and so
        // call this specialized manifest factory method.
        Package manifest = factory.createSpecialDefaultPackageManifest();
        manifest.setVersion(serviceLocator.getProjectService().getLastSupportedEndpointVersion());


        monitorCheckSubTask(monitor, "Loading deployment candidates...");
        DeploymentComponentSet deploymentComponents = deploymentPayload.getDeploySelectedComponents();

        IProject project = deploymentPayload.getProject();
        Package packageManifest = serviceLocator.getProjectService().getPackageManifestFactory().getPackageManifest(project);
       
        // Looping through each component that we want to deploy.
        //
        // Custom object components are handled differently.
        for (DeploymentComponent deploymentComponent : deploymentComponents) {
            if (deploymentComponent.getDestinationSummary().equals(DeploymentSummary.DELETED)) {
                projectPackageList.addDeleteComponent(deploymentComponent.getComponent());
            } else {
                projectPackageList.addComponent(deploymentComponent.getComponent());
              
                // If this is a custom object component, check to see if it's actually in the manifest -
                // maybe a field inside the object was retrieved from the source org into the project,
                // but the object itself wasn't.
                //
                // If so, we don't want to add the object to the deployment manifest.  Any fields
                // will be included by default because the manifest has a "*" for that component type.
                // (In fact it has a "*" for every component type except custom object, because of
                // this particular issue. See changelist 1452239.)
                //
                //Package packageManifest = serviceLocator.getProjectService().getPackageManifestFactory().getPackageManifest(project);
        String type = deploymentComponent.getComponent().getComponentType();
                boolean isObject = type.equals(Constants.CUSTOM_OBJECT);
                boolean isSharingRule = Constants.ABSTRACT_SHARING_RULE_TYPES.contains(type) || Constants.SHARING_RULE_TYPES.contains(type);
        if(isObject) {
          boolean isPresent = new PackageManifest(packageManifest).contains("CustomObject", deploymentComponent.getComponent().getName());
          if (isPresent) {
            factory.addComponentToManifest(manifest, deploymentComponent.getComponent());
          }
        }
        //If it's a sharing rule, we will add it from the project manifest
        else if (!isSharingRule) {// && !type.equals("Settings")) {
          factory.addComponentToManifest(manifest, deploymentComponent.getComponent());
        }

            }
        }
       
        //Add sharing rules from the package manifest file since the component and file structure is
        //different than what is required by the package manifest file.
        //W-1169372
        for (PackageTypeMembers member : packageManifest.getTypes()) {
          if (Constants.SHARING_RULE_TYPES.contains(member.getName())) {
            manifest.getTypes().add(member);
          }
        }
        monitorWork(monitor);

        try {
            Component comp = factory.getComponentFactory().getComponentById(Constants.PACKAGE_MANIFEST);
            comp.setMetadataFilePath(Constants.PACKAGE_MANIFEST_FILE_NAME);
            comp.setBody(manifest.getXMLString());
            comp.setName(manifest.getFullName());

            projectPackageList.get(0).removePackageManifestComponent();
            projectPackageList.get(0).setPackageManifest(comp);
            projectPackageList.get(0).addComponent(comp);
        } catch (Exception e) {
            logger.warn("Unable to change deploy manifest", e);
        }

        // prepare result and record datetime
        DeploymentResult result = new DeploymentResult();
        result.setDeployTime(new GregorianCalendar());
        result.setDeploymentPayload(deploymentPayload);
        result.setDestinationOrg(connection.getConnectionInfo());
        result.setSourceProjectName(deploymentPayload.getProject().getName());
        result.setSourceEndpoint(serviceLocator.getProjectService().getSoapEndPoint(deploymentPayload.getProject()));
        result.setSourceUsername(serviceLocator.getProjectService().getUsername(deploymentPayload.getProject()));

        DeployOptions deployOptions = makeDefaultDeployOptions(checkOnly, serviceLocator.getPackageDeployService());

        LogInfo[] runTestLogSettings =
                serviceLocator.getLoggingService().getAllLogInfo(getProject(), LoggingInfo.SupportedFeatureEnum.RunTest);

        DeployResultExt deployResultHandler = null;
        try {
            deployResultHandler =
                    serviceLocator.getPackageDeployService().deploy(connection, projectPackageList, deployOptions, runTestLogSettings,
                        false, new SubProgressMonitor(monitor, 6));
        } catch (ServiceTimeoutException ex) {
            deployResultHandler = serviceLocator.getPackageDeployService().handleDeployServiceTimeoutException(ex, "deploy", monitor);
        }

        result.setDeployResultHandler(deployResultHandler);

        if (deployResultHandler != null && !deployResultHandler.isSuccess()) {
            StringBuffer strBuff = new StringBuffer("Deployment FAILED.  ");
            DeployMessageExt messageHandler = deployResultHandler.getMessageHandler();
            if (messageHandler != null) {
                messageHandler.logMessage(strBuff, false);
            }
            logger.warn(strBuff.toString());
        }

        return result;
    }

  protected DeployOptions makeDefaultDeployOptions(boolean checkOnly, PackageDeployService packageDeployService) throws ForceConnectionException {
        DeployOptions deployOptions = packageDeployService.getDeployOptions(checkOnly);
        deployOptions.setIgnoreWarnings(true);
        return deployOptions;
    }

}
TOP

Related Classes of com.salesforce.ide.deployment.ui.wizards.DeploymentController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.