org.cloudifysource.rest.controllers.TemplatesController.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudifysource.rest.controllers.TemplatesController.java

Source

/*******************************************************************************
 * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *******************************************************************************/
package org.cloudifysource.rest.controllers;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;

import org.apache.commons.collections.ListUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.cloudifysource.domain.ComputeTemplateHolder;
import org.cloudifysource.domain.cloud.Cloud;
import org.cloudifysource.domain.cloud.compute.ComputeTemplate;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.cloudifysource.dsl.internal.CloudifyErrorMessages;
import org.cloudifysource.dsl.internal.CloudifyMessageKeys;
import org.cloudifysource.dsl.internal.DSLException;
import org.cloudifysource.dsl.internal.DSLReader;
import org.cloudifysource.dsl.internal.DSLUtils;
import org.cloudifysource.dsl.rest.AddTemplatesException;
import org.cloudifysource.dsl.rest.request.AddTemplatesInternalRequest;
import org.cloudifysource.dsl.rest.request.AddTemplatesRequest;
import org.cloudifysource.dsl.rest.response.AddTemplateResponse;
import org.cloudifysource.dsl.rest.response.AddTemplatesInternalResponse;
import org.cloudifysource.dsl.rest.response.AddTemplatesResponse;
import org.cloudifysource.dsl.rest.response.AddTemplatesStatus;
import org.cloudifysource.dsl.rest.response.GetTemplateResponse;
import org.cloudifysource.dsl.rest.response.ListTemplatesResponse;
import org.cloudifysource.dsl.rest.response.RemoveTemplatesResponse;
import org.cloudifysource.dsl.utils.IPUtils;
import org.cloudifysource.rest.RestConfiguration;
import org.cloudifysource.rest.internal.RestClientInternal;
import org.cloudifysource.rest.repo.UploadRepo;
import org.cloudifysource.rest.util.RestUtils;
import org.cloudifysource.rest.validators.AddTemplatesValidationContext;
import org.cloudifysource.rest.validators.AddTemplatesValidator;
import org.cloudifysource.rest.validators.TemplatesValidationContext;
import org.cloudifysource.rest.validators.TemplatesValidator;
import org.cloudifysource.restDoclet.annotations.InternalMethod;
import org.cloudifysource.restclient.exceptions.RestClientException;
import org.cloudifysource.restclient.messages.MessagesUtils;
import org.cloudifysource.security.CustomPermissionEvaluator;
import org.cloudifysource.utilitydomain.data.reader.ComputeTemplatesReader;
import org.openspaces.admin.Admin;
import org.openspaces.admin.pu.ProcessingUnit;
import org.openspaces.admin.pu.ProcessingUnitInstance;
import org.openspaces.admin.pu.ProcessingUnits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.j_spaces.kernel.PlatformVersion;

/**
 * @author yael
 * @since 2.7.0
 * 
 */
@Controller
@RequestMapping(value = "/{version}/templates")
public class TemplatesController extends BaseRestController {
    private static final Logger logger = Logger.getLogger(TemplatesController.class.getName());

    @Autowired
    private RestConfiguration restConfig;
    @Autowired
    private UploadRepo repo;
    @Autowired
    private final AddTemplatesValidator[] addTemplatesValidators = new AddTemplatesValidator[0];
    @Autowired
    private final TemplatesValidator[] templatesValidators = new TemplatesValidator[0];
    private Cloud cloud;
    private Admin admin;
    private CustomPermissionEvaluator permissionEvaluator;
    private File cloudConfigurationDir;

    /**
     * Initialization.
     */
    @PostConstruct
    public void init() {
        log(Level.INFO, "Initializing Templates controller.");
        cloud = restConfig.getCloud();
        admin = restConfig.getAdmin();
        permissionEvaluator = restConfig.getPermissionEvaluator();
        cloudConfigurationDir = restConfig.getCloudConfigurationDir();
    }

    /**
     * Add templates from templates folder to the cloud. Returns a response in case of success or partial failure.
     * 
     * @param request
     *            {@link AddTemplatesRequest}
     * @return {@link AddTemplatesResponse}
     * @throws RestErrorException
     *             if failed to validate the addTemplates request.
     * @throws IOException
     *             If failed to unzip templates folder.
     * @throws DSLException
     *             If failed to read the templates from templates folder.
     * @throws AddTemplatesException 
     *             If failed to add templates (failure or partial failure).
     */
    @PreAuthorize("isFullyAuthenticated() and hasAnyRole('ROLE_CLOUDADMINS')")
    @RequestMapping(method = RequestMethod.POST)
    public AddTemplatesResponse addTemplates(@RequestBody final AddTemplatesRequest request)
            throws RestErrorException, IOException, DSLException, AddTemplatesException {
        log(Level.INFO, "[addTemplates] - starting add templates.");

        // validate
        validateAddTemplates(request);
        File templatesZippedFolder = null;
        try {
            // get templates folder
            final String uploadKey = request.getUploadKey();
            templatesZippedFolder = repo.get(uploadKey);
            if (templatesZippedFolder == null) {
                throw new RestErrorException(CloudifyMessageKeys.WRONG_TEMPLATES_UPLOAD_KEY.getName(), uploadKey);
            }
            final AddTemplatesInternalRequest internalRequest = createInternalRequest(request,
                    templatesZippedFolder);
            final List<String> expectedTemplates = internalRequest.getExpectedTemplates();
            log(Level.INFO, "expecting to add " + expectedTemplates.size() + " templates: " + expectedTemplates);
            // add the templates to all REST instances
            final AddTemplatesResponse addTemplatesToRestInstances = addTemplatesToRestInstances(internalRequest,
                    templatesZippedFolder);
            handleAddTemplatesResponse(addTemplatesToRestInstances);
            return addTemplatesToRestInstances;
        } finally {
            if (templatesZippedFolder != null) {
                FileUtils.deleteQuietly(templatesZippedFolder);
            }
        }

    }

    private void handleAddTemplatesResponse(final AddTemplatesResponse addTemplatesResponse)
            throws AddTemplatesException {
        final Map<String, AddTemplateResponse> templatesResponse = addTemplatesResponse.getTemplates();

        boolean atLeastOneFailed = false;
        boolean atLeastOneSucceeded = false;
        for (final AddTemplateResponse templateResponse : templatesResponse.values()) {
            final Map<String, String> failedToAddHosts = templateResponse.getFailedToAddHosts();
            if (failedToAddHosts != null && !failedToAddHosts.isEmpty()) {
                atLeastOneFailed = true;
                if (atLeastOneSucceeded) {
                    break;
                }
            }
            final List<String> successfullyAddedHosts = templateResponse.getSuccessfullyAddedHosts();
            if (successfullyAddedHosts != null && !successfullyAddedHosts.isEmpty()) {
                atLeastOneSucceeded = true;
                if (atLeastOneFailed) {
                    break;
                }
            }
        }
        /*
         * partial failure or failure
         */
        if (atLeastOneFailed) {
            if (atLeastOneSucceeded) {
                // partial
                log(Level.WARNING, "[addTemplates] - Partial failure: " + templatesResponse);
                addTemplatesResponse.setStatus(AddTemplatesStatus.PARTIAL_FAILURE);
                throw new AddTemplatesException(addTemplatesResponse);
            }
            // failure
            log(Level.WARNING, "[addTemplates] - Failed to add all templates: " + templatesResponse);
            addTemplatesResponse.setStatus(AddTemplatesStatus.FAILURE);
            throw new AddTemplatesException(addTemplatesResponse);
        }
        addTemplatesResponse.setStatus(AddTemplatesStatus.SUCCESS);
        log(Level.INFO, "[addTemplatesToRestInstances] - successfully added all templates to all ("
                + addTemplatesResponse.getInstances().size() + ") REST instances.");

    }

    /**
     * Get the cloud's templates.
     * 
     * @return {@link ListTemplatesResponse} containing the cloud's templates.
     * @throws RestErrorException
     *             If cloud is a local cloud.
     */
    @RequestMapping(method = RequestMethod.GET)
    @PreAuthorize("isFullyAuthenticated() and hasAnyRole('ROLE_CLOUDADMINS', 'ROLE_APPMANAGERS')")
    public ListTemplatesResponse listTemplates() throws RestErrorException {
        validateTemplateOperation("list-templates");
        final ListTemplatesResponse response = new ListTemplatesResponse();
        final Map<String, ComputeTemplate> templates = cloud.getCloudCompute().getTemplates();
        log(Level.FINE, "listTemplates found " + templates.size() + " templates: " + templates);
        response.setTemplates(templates);
        return response;
    }

    /**
     * Get template from the cloud.
     * 
     * @param templateName
     *            The name of the template to get.
     * @return a map containing the template and a success status if succeeded, else returns an error status.
     * @throws RestErrorException
     *             if the cloud is a local cloud or the template doesn't exist.
     */
    @RequestMapping(value = "{templateName}", method = RequestMethod.GET)
    @PreAuthorize("isFullyAuthenticated() and hasAnyRole('ROLE_CLOUDADMINS', 'ROLE_APPMANAGERS')")
    public GetTemplateResponse getTemplate(@PathVariable final String templateName) throws RestErrorException {

        validateTemplateOperation("get-template");

        // get template from cloud
        final ComputeTemplate cloudTemplate = cloud.getCloudCompute().getTemplates().get(templateName);

        if (cloudTemplate == null) {
            log(Level.WARNING, "[getTemplate] - template [" + templateName + "] not found. cloud templates list: "
                    + cloud.getCloudCompute().getTemplates());
            throw new RestErrorException(CloudifyErrorMessages.TEMPLATE_NOT_EXIST.getName(), templateName);
        }
        final GetTemplateResponse response = new GetTemplateResponse();
        response.setTemplate(cloudTemplate);
        return response;
    }

    private AddTemplatesInternalRequest createInternalRequest(final AddTemplatesRequest request,
            final File templatesZippedFolder) throws DSLException, IOException {

        final AddTemplatesInternalRequest internalRequest = new AddTemplatesInternalRequest();
        // cloud templates
        final File unzippedFolder = new ComputeTemplatesReader().unzipCloudTemplatesFolder(templatesZippedFolder);
        try {
            final List<ComputeTemplateHolder> cloudTemplatesHolders = new ComputeTemplatesReader()
                    .readCloudTemplatesFromDirectory(unzippedFolder);
            internalRequest.setCloudTemplates(cloudTemplatesHolders);
            // expected templates
            final List<String> expectedAddedTemplates = new LinkedList<String>();
            for (final ComputeTemplateHolder templateHolder : cloudTemplatesHolders) {
                expectedAddedTemplates.add(templateHolder.getName());
            }
            internalRequest.setExpectedTemplates(expectedAddedTemplates);

            return internalRequest;

        } finally {
            if (unzippedFolder != null) {
                FileUtils.deleteQuietly(unzippedFolder);
            }
        }
    }

    /**
     * For each puInstance - send the invoke an add templates request.
     * 
     * @param templatesFolder
     *            .
     * @param expectedTemplates
     *            The expected templates to add.
     * @param addedTemplatesByHost
     *            a map updates by this method to specify the failed to add templates for each instance.
     * @param failedToAddTemplatesByHost
     *            a map updates by this method to specify the failed to add templates for each instance.
     */
    private AddTemplatesResponse addTemplatesToRestInstances(final AddTemplatesInternalRequest request,
            final File templatesZippedFolder) {

        final Map<String, AddTemplateResponse> templatesResponse = new HashMap<String, AddTemplateResponse>();

        // get the instances
        final ProcessingUnitInstance[] instances = admin.getProcessingUnits()
                .waitFor("rest", RestUtils.TIMEOUT_IN_SECOND, TimeUnit.SECONDS).getInstances();
        final List<String> instancesList = new ArrayList<String>(instances.length);
        // execute add-template on each rest instance
        log(Level.INFO, "[addTemplatesToRestInstances] - sending add-templates request to " + instances.length
                + " instances.");
        for (final ProcessingUnitInstance puInstance : instances) {
            final String hostAddress = puInstance.getMachine().getHostAddress();
            instancesList.add(hostAddress);
            log(Level.INFO, "[addTemplatesToRestInstances] - sending request to " + hostAddress);
            /*
             * add template to instance and get the response
             */
            final AddTemplatesInternalResponse instanceResponse = executeAddTemplateOnInstance(hostAddress,
                    Integer.toString(puInstance.getJeeDetails().getPort()), request, templatesZippedFolder);
            final Map<String, String> failedToAddTempaltesToHost = instanceResponse
                    .getFailedToAddTempaltesAndReasons();
            final List<String> addedTempaltes = instanceResponse.getAddedTempaltes();
            /*
             * failed to add templates
             */
            if (failedToAddTempaltesToHost != null) {
                for (final Entry<String, String> entry : failedToAddTempaltesToHost.entrySet()) {
                    log(Level.WARNING, "[addTemplatesToRestInstances] - failed to add templates to host ["
                            + hostAddress + "]: " + failedToAddTempaltesToHost);
                    // update template's entry in the final response
                    // for each template - add the current host to the failure hosts map of the template.
                    String templateName = entry.getKey();
                    AddTemplateResponse addTemplateResponse = templatesResponse.get(templateName);
                    // create new response if the template doesn't have one yet.
                    if (addTemplateResponse == null) {
                        addTemplateResponse = new AddTemplateResponse();
                    }
                    // get the failure map (hosts and reasons).
                    Map<String, String> failedHostsReasons = addTemplateResponse.getFailedToAddHosts();
                    if (failedHostsReasons == null) {
                        failedHostsReasons = new HashMap<String, String>();
                    }
                    // add the failed host (and failure reason) to the failure map.
                    failedHostsReasons.put(hostAddress, entry.getValue());
                    // set the updated failure map at template's response.
                    addTemplateResponse.setFailedToAddHosts(failedHostsReasons);
                    // add the template and its response to the final templates response.
                    templatesResponse.put(templateName, addTemplateResponse);
                }
            }
            /*
             * successfully added templates
             */
            if (addedTempaltes != null) {
                log(Level.INFO, "[addTemplatesToRestInstances] - successfully added templates to host ["
                        + hostAddress + "]: " + addedTempaltes);
                for (final String templateName : addedTempaltes) {
                    AddTemplateResponse addTemplateResponse = templatesResponse.get(templateName);
                    // create new response if the template doesn't have one yet.
                    if (addTemplateResponse == null) {
                        addTemplateResponse = new AddTemplateResponse();
                    }
                    // get the successfully hosts list.
                    List<String> successfullyAddedHosts = addTemplateResponse.getSuccessfullyAddedHosts();
                    if (successfullyAddedHosts == null) {
                        successfullyAddedHosts = new LinkedList<String>();
                    }
                    // add the host to the successfully added hosts list.
                    successfullyAddedHosts.add(hostAddress);
                    // set the updated list at template's response.
                    addTemplateResponse.setSuccessfullyAddedHosts(successfullyAddedHosts);
                    // add the template and its response to the final templates response.
                    templatesResponse.put(templateName, addTemplateResponse);
                }
            }
        }

        // create and return the response (the status of the response will be set later).
        final AddTemplatesResponse response = new AddTemplatesResponse();
        response.setInstances(instancesList);
        response.setTemplates(templatesResponse);
        return response;
    }

    /**
     * Invoke add templates on the given instance.
     * 
     * @param puInstance
     * @param request
     * @param host
     * @return AddTemplatesInternalResponse
     */
    private AddTemplatesInternalResponse executeAddTemplateOnInstance(final String host, final String port,
            final AddTemplatesInternalRequest request, final File templatesZippedFolder) {
        AddTemplatesInternalResponse instanceResponse;
        String requestName = "create rest client";
        try {
            // invoke upload and add-templates commands on each REST instance.
            /*
             * create rest client
             */
            final RestClientInternal client = createRestClientInternal(host, port);
            requestName = "execute upload request";
            /*
             * upload
             */
            String uploadKey = client.uploadInternal(null, templatesZippedFolder).getUploadKey();
            log(Level.FINE, "[executeAddTemplateOnInstance] - Uploaded templates zipped folder ["
                    + templatesZippedFolder + "] to host [" + host + "], upload key = " + uploadKey);
            request.setUploadKey(uploadKey);
            requestName = "execute add-templates-internal request";
            /*
             * add templates
             */
            instanceResponse = client.addTemplatesInternal(request);
        } catch (final RestClientException e) {
            // the request failed => all expected templates failed to be added
            // create a response that contains all expected templates in a failure map.
            log(Level.WARNING, "[executeAddTemplateOnInstance] - Failed to " + requestName + " to " + host
                    + ". Error message: " + e.getMessageFormattedText() + ", verbose: " + e.getVerbose());
            final Map<String, String> failedMap = new HashMap<String, String>();
            for (final String expectedTemplate : request.getExpectedTemplates()) {
                failedMap.put(expectedTemplate, "http request failed [" + e.getMessageFormattedText() + "]");
            }
            instanceResponse = new AddTemplatesInternalResponse();
            instanceResponse.setFailedToAddTempaltesAndReasons(failedMap);
            return instanceResponse;
        }
        final List<String> addedTempaltes = instanceResponse.getAddedTempaltes();
        log(Level.FINE, "[executeAddTemplateOnInstance] - added " + addedTempaltes.size() + " templates: "
                + addedTempaltes);
        final Map<String, String> failedToAddTempaltesAndReasons = instanceResponse
                .getFailedToAddTempaltesAndReasons();
        final List<String> failedList = new ArrayList<String>(failedToAddTempaltesAndReasons.keySet());
        log(Level.FINE, "[executeAddTemplateOnInstance] - failed to add " + failedList.size() + " templates: "
                + failedList);
        // addedTempaltes and failedList suppose to contain all templates from expectedTemplates.
        final List<?> union = ListUtils.union(addedTempaltes, failedList);
        final List<?> subtract = ListUtils.subtract(request.getExpectedTemplates(), union);
        if (!subtract.isEmpty()) {
            // add all missing templates to the failure map.
            for (final Object templateName : subtract) {
                failedToAddTempaltesAndReasons.put((String) templateName,
                        "expected template missing (not found in failure list)");
            }
            instanceResponse.setFailedToAddTempaltesAndReasons(failedToAddTempaltesAndReasons);
        }
        return instanceResponse;
    }

    /**
     * Add template files to the cloud configuration directory and to the cloud object. This method supposed to be
     * invoked by the MNG on all REST instances.
     * 
     * @param request
     *            The request.
     * @return {@link AddTemplatesInternalResponse}
     * @throws IOException
     *             in case of reading error.
     * @throws RestErrorException .
     */
    @InternalMethod
    @RequestMapping(value = "internal", method = RequestMethod.POST)
    public AddTemplatesInternalResponse addTemplatesInternal(@RequestBody final AddTemplatesInternalRequest request)
            throws IOException, RestErrorException {

        final ComputeTemplatesReader reader = new ComputeTemplatesReader();
        String uploadKey = request.getUploadKey();
        final File templatesFolder = repo.get(uploadKey);
        if (templatesFolder == null) {
            throw new RestErrorException(CloudifyMessageKeys.WRONG_TEMPLATES_UPLOAD_KEY.getName(), uploadKey);
        }
        final File unzippedTemplatesFolder = reader.unzipCloudTemplatesFolder(templatesFolder);

        try {
            log(Level.INFO, "[addTemplatesInternal] - adding templates " + request.getExpectedTemplates());
            // add templates to the cloud and return the added templates.
            return addTemplatesToCloud(unzippedTemplatesFolder, request.getCloudTemplates());
        } finally {
            FileUtils.deleteQuietly(unzippedTemplatesFolder);
        }
    }

    /**
     * Adds templates to cloud's templates. Adds templates' files to cloud configuration directory.
     * 
     * @param templatesFolder
     * @return {@link AddTemplatesInternalResponse}
     */
    private AddTemplatesInternalResponse addTemplatesToCloud(final File templatesFolder,
            final List<ComputeTemplateHolder> templatesHolders) {
        log(Level.FINE, "[addTemplatesToCloud] - Adding " + templatesHolders.size() + " templates to cloud.");
        // adds the templates to the cloud's templates list, deletes failed to added templates from the folder.
        final AddTemplatesInternalResponse addTemplatesToCloudListresponse = addTemplatesToCloudList(
                templatesFolder, templatesHolders);
        List<String> addedTemplates = addTemplatesToCloudListresponse.getAddedTempaltes();
        final Map<String, String> failedToAddTemplates = addTemplatesToCloudListresponse
                .getFailedToAddTempaltesAndReasons();
        // if no templates were added, throw an exception
        if (addedTemplates.isEmpty()) {
            log(Level.WARNING,
                    "[addTemplatesToCloud] - Failed to add templates from " + templatesFolder.getAbsolutePath());
        } else {
            // at least one template was added, copy files from template folder to a new folder.
            log(Level.FINE,
                    "[addTemplatesToCloud] - Coping templates files from " + templatesFolder.getAbsolutePath()
                            + " to a new folder under " + cloudConfigurationDir.getAbsolutePath());
            try {
                final File localTemplatesDir = copyTemplateFilesToCloudConfigDir(templatesFolder);
                log(Level.FINE, "[addTemplatesToCloud] - The templates files were copied to "
                        + localTemplatesDir.getAbsolutePath());
                updateCloudTemplatesUploadPath(addedTemplates, localTemplatesDir);
            } catch (final IOException e) {
                // failed to copy files - remove all added templates from cloud and them to the failed map.
                log(Level.WARNING,
                        "[addTemplatesToCloud] - Failed to copy templates files, error: " + e.getMessage(), e);
                for (final String templateName : addedTemplates) {
                    cloud.getCloudCompute().getTemplates().remove(templateName);
                    failedToAddTemplates.put(templateName, "failed to copy templates files");
                }
                // added templates should not include templates.
                addedTemplates = new LinkedList<String>();
            }
        }
        if (!failedToAddTemplates.isEmpty()) {
            log(Level.WARNING, "[addTemplatesToCloud] - Failed to add the following templates: "
                    + failedToAddTemplates.toString());
        }
        // create and return the result.
        final AddTemplatesInternalResponse response = new AddTemplatesInternalResponse();
        response.setAddedTempaltes(addedTemplates);
        response.setFailedToAddTempaltesAndReasons(failedToAddTemplates);
        return response;
    }

    /**
     * Updates the upload local path in all added cloud templates.
     * 
     * @param addedTemplates
     *            the added templates.
     * @param localTemplatesDir
     *            the directory where the upload directory expected to be found.
     */
    private void updateCloudTemplatesUploadPath(final List<String> addedTemplates, final File localTemplatesDir) {
        for (final String templateName : addedTemplates) {
            final ComputeTemplate cloudTemplate = cloud.getCloudCompute().getTemplates().get(templateName);
            final String localUploadPath = new File(localTemplatesDir, cloudTemplate.getLocalDirectory())
                    .getAbsolutePath();
            cloudTemplate.setAbsoluteUploadDir(localUploadPath);
        }
    }

    /**
     * Scans the cloudTemplatesHolders list and adds each template that doesn't already exist. Rename template's file if
     * needed (if its prefix is not the template's name).
     * 
     * @param templatesFolder
     *            the folder contains templates files.
     * @param cloudTemplates
     *            the list of cloud templates.
     * @param addedTemplates
     *            a list for this method to update with all the added templates.
     * @param failedToAddTemplates
     *            a list for this method to update with all the failed to add templates.
     */
    private AddTemplatesInternalResponse addTemplatesToCloudList(final File templatesFolder,
            final List<ComputeTemplateHolder> cloudTemplates) {
        final List<String> addedTemplates = new LinkedList<String>();
        final Map<String, String> failedToAddTemplates = new HashMap<String, String>();
        log(Level.FINE,
                "[addTemplatesToCloudList] - adding " + cloudTemplates.size() + " templates to cloud's list.");
        for (final ComputeTemplateHolder holder : cloudTemplates) {
            final String templateName = holder.getName();
            final String originalTemplateFileName = holder.getTemplateFileName();
            // check if template already exist
            final Map<String, ComputeTemplate> templates = cloud.getCloudCompute().getTemplates();
            if (templates.containsKey(templateName)) {
                // template already exists
                log(Level.WARNING, "[addTemplatesToCloudList] - Template already exists: " + templateName);
                failedToAddTemplates.put(templateName, "template already exists");
                new File(templatesFolder, originalTemplateFileName).delete();
                continue;
            }

            // rename template file to <templateName>-template.groovy if needed
            // rename the properties and overrides files as well.
            try {
                renameTemplateFilesIfNeeded(templatesFolder, holder);
            } catch (final IOException e) {
                failedToAddTemplates.put(templateName,
                        "failed to rename template's file. error: " + e.getMessage());
                // rename failed - delete the file so it wont be added to the additional templates folder.
                log(Level.WARNING, "[renameTemplateFileIfNeeded] - Failed to rename template's file."
                        + " The file [" + originalTemplateFileName + "] will be deleted.", e);
                new File(templatesFolder, originalTemplateFileName).delete();
                continue;
            }

            // add template to cloud templates list
            final ComputeTemplate cloudTemplate = holder.getCloudTemplate();
            templates.put(templateName, cloudTemplate);
            addedTemplates.add(templateName);
        }
        final AddTemplatesInternalResponse response = new AddTemplatesInternalResponse();
        response.setAddedTempaltes(addedTemplates);
        response.setFailedToAddTempaltesAndReasons(failedToAddTemplates);
        return response;
    }

    /**
     * Copies all the files from templatesFolder to a new directory under cloud configuration directory.
     * 
     * @param templatesDirToCopy
     *            the directory contains all the files to copy.
     * @throws IOException
     *             If failed to copy files.
     */
    private File copyTemplateFilesToCloudConfigDir(final File templatesDirToCopy) throws IOException {
        final File templatesDirParent = restConfig.getAdditionalTempaltesFolder();
        // create new templates folder with a unique name.
        String folderName = CloudifyConstants.TEMPLATE_FOLDER_PREFIX
                + restConfig.getLastTemplateFileNum().incrementAndGet();
        File copiedtemplatesFolder = new File(templatesDirParent, folderName);
        while (copiedtemplatesFolder.exists()) {
            folderName = CloudifyConstants.TEMPLATE_FOLDER_PREFIX
                    + restConfig.getLastTemplateFileNum().incrementAndGet();
            copiedtemplatesFolder = new File(templatesDirParent, folderName);
        }
        copiedtemplatesFolder.mkdir();
        try {
            FileUtils.copyDirectory(templatesDirToCopy, copiedtemplatesFolder);
            return copiedtemplatesFolder;
        } catch (final IOException e) {
            FileUtils.deleteDirectory(copiedtemplatesFolder);
            restConfig.getLastTemplateFileNum().decrementAndGet();
            throw e;
        }
    }

    /**
     * If the original template's file name prefix is not the template's name, rename it. Also, rename the properties
     * and overrides files if exist.
     * 
     * @param templatesFolder
     *            the folder that contains the template's file.
     * @param holder
     *            holds the relevant template
     * @throws IOException
     *             If failed to rename.
     */

    private void renameTemplateFilesIfNeeded(final File templatesFolder, final ComputeTemplateHolder holder)
            throws IOException {
        final String templateName = holder.getName();

        final String templateFileName = holder.getTemplateFileName();
        final File templateFile = new File(templatesFolder, templateFileName);
        final String propertiesFileName = holder.getPropertiesFileName();
        final String overridesFileName = holder.getOverridesFileName();

        log(Level.FINE,
                "[renameTemplateFileIfNeeded] - Renaming template files [template name = " + templateName + "]");

        // rename groovy file if needed
        String newName = DSLUtils.renameCloudTemplateFileNameIfNeeded(templateFile, templateName,
                DSLUtils.TEMPLATE_DSL_FILE_NAME_SUFFIX);
        if (newName != null) {
            log(Level.FINE, "[renameTemplateFileIfNeeded] - Renamed template file name from " + templateFileName
                    + " to " + newName + ".");
        }
        // rename properties file if needed
        if (propertiesFileName != null) {
            final File propertiesFile = new File(templatesFolder, propertiesFileName);
            newName = DSLUtils.renameCloudTemplateFileNameIfNeeded(propertiesFile, templateName,
                    DSLUtils.TEMPLATES_PROPERTIES_FILE_NAME_SUFFIX);
            if (newName != null) {
                log(Level.FINE, "[renameTemplateFileIfNeeded] - Renamed template's properties file name from" + " "
                        + propertiesFileName + " to " + newName + ".");
            }
        }
        // rename overrides file if needed
        if (overridesFileName != null) {
            final File overridesFile = new File(templatesFolder, overridesFileName);
            newName = DSLUtils.renameCloudTemplateFileNameIfNeeded(overridesFile, templateName,
                    DSLUtils.TEMPLATES_OVERRIDES_FILE_NAME_SUFFIX);
            if (newName != null) {
                log(Level.FINE, "[renameTemplateFileIfNeeded] - Renamed template's overrides file name from "
                        + overridesFileName + " to " + newName + ".");
            }
        }

    }

    private void validateAddTemplates(final AddTemplatesRequest request) throws RestErrorException {
        final AddTemplatesValidationContext validationContext = new AddTemplatesValidationContext();
        validationContext.setCloud(cloud);
        validationContext.setRequest(request);
        for (final AddTemplatesValidator validator : addTemplatesValidators) {
            validator.validate(validationContext);
        }
    }

    private void validateTemplateOperation(final String opName) throws RestErrorException {
        final TemplatesValidationContext validationContext = new TemplatesValidationContext();
        validationContext.setCloud(cloud);
        validationContext.setOperationName(opName);
        for (final TemplatesValidator validator : templatesValidators) {
            validator.validate(validationContext);
        }
    }

    /**
     * Removes a template from the cloud.
     * 
     * @param templateName
     *            The name of the template to remove.
     * @throws RestErrorException
     *             If cloud is a local cloud or one of the REST instances failed to remove the template.
     */
    @PreAuthorize("isFullyAuthenticated() and hasAnyRole('ROLE_CLOUDADMINS')")
    @RequestMapping(value = "{templateName}", method = RequestMethod.DELETE)
    public void removeTemplate(@PathVariable final String templateName) throws RestErrorException {

        validateTemplateOperation("remove-template");

        log(Level.INFO, "[removeTemplate] - starting remove template [" + templateName + "]");

        // check if the template is being used by at least one service, so it cannot be removed.
        final List<String> templateServices = getTemplateServices(templateName);
        if (!templateServices.isEmpty()) {
            log(Level.WARNING,
                    "[removeTemplate] - failed to remove template [" + templateName
                            + "]. The template is being used by " + templateServices.size() + " services: "
                            + templateServices);
            throw new RestErrorException(CloudifyErrorMessages.TEMPLATE_IN_USE.getName(), templateName,
                    templateServices);
        }

        // remove template from all REST instances
        final RemoveTemplatesResponse resposne = removeTemplateFromRestInstances(templateName);
        handleRemoveTemplateResponse(resposne, templateName);
        log(Level.INFO, "[removeTemplate] - successfully removed template [" + templateName + "].");
    }

    private void handleRemoveTemplateResponse(final RemoveTemplatesResponse resposne, final String templateName)
            throws RestErrorException {
        final Map<String, String> failedToRemoveFromHosts = resposne.getFailedToRemoveFromHosts();
        final List<String> successfullyRemovedFromHosts = resposne.getSuccessfullyRemovedFromHosts();

        // check if some REST instances failed to remove the template
        if (!failedToRemoveFromHosts.isEmpty()) {
            String message = "[removeTemplate] - failed to remove template [" + templateName + "] from: "
                    + failedToRemoveFromHosts;
            if (!successfullyRemovedFromHosts.isEmpty()) {
                message += ". Succeeded to remove the template from: " + successfullyRemovedFromHosts;
            }
            log(Level.WARNING, message);
            throw new RestErrorException(CloudifyErrorMessages.FAILED_REMOVE_TEMPLATE.getName(), templateName,
                    failedToRemoveFromHosts.toString());
        }
    }

    private List<String> getTemplateServices(final String templateName) {
        final List<String> services = new LinkedList<String>();
        final ProcessingUnits processingUnits = admin.getProcessingUnits();
        for (final ProcessingUnit processingUnit : processingUnits) {
            final Properties puProps = processingUnit.getBeanLevelProperties().getContextProperties();
            final String puTemplateName = puProps.getProperty(CloudifyConstants.CONTEXT_PROPERTY_TEMPLATE);
            if (puTemplateName != null && puTemplateName.equals(templateName)) {
                services.add(processingUnit.getName());
            }
        }
        return services;
    }

    private RemoveTemplatesResponse removeTemplateFromRestInstances(final String templateName) {
        // get rest instances
        final ProcessingUnit processingUnit = admin.getProcessingUnits().waitFor("rest",
                RestUtils.TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
        final ProcessingUnitInstance[] instances = processingUnit.getInstances();
        // invoke remove-template command on each REST instance.
        log(Level.INFO, "[removeTemplateFromRestInstances] - sending remove-template request to " + instances.length
                + " REST instances.");
        final Map<String, String> failedToRemoveFromHosts = new HashMap<String, String>();
        final List<String> successfullyRemovedFromHosts = new LinkedList<String>();
        for (final ProcessingUnitInstance puInstance : instances) {
            final String hostAddress = puInstance.getMachine().getHostAddress();
            final String port = Integer.toString(puInstance.getJeeDetails().getPort());
            try {
                final RestClientInternal client = createRestClientInternal(hostAddress, port);
                log(Level.INFO, "sending request to " + hostAddress);
                client.removeTemplateInternal(templateName);
            } catch (final RestClientException e) {
                failedToRemoveFromHosts.put(hostAddress, e.getMessageFormattedText());
                log(Level.WARNING, "[removeTemplateFromRestInstances] - remove template [" + templateName
                        + "] from instance [" + hostAddress + "] failed. Error: " + e.getMessageFormattedText(), e);
                continue;
            }
            successfullyRemovedFromHosts.add(hostAddress);
            log(Level.INFO, "[removeTemplateFromRestInstances] - Successfully removed template [" + templateName
                    + "] from " + hostAddress);
        }
        final RemoveTemplatesResponse response = new RemoveTemplatesResponse();
        response.setFailedToRemoveFromHosts(failedToRemoveFromHosts);
        response.setSuccessfullyRemovedFromHosts(successfullyRemovedFromHosts);
        return response;
    }

    /**
     * Internal method. Remove template file from the cloud configuration directory and from the cloud's templates map.
     * This method supposed to be invoked from removeTemplate of a REST instance.
     * 
     * @param templateName
     *            the name of the template to remove.
     * @throws RestErrorException
     *             If failed to remove the template.
     */
    @InternalMethod
    @RequestMapping(value = "internal/{templateName}", method = RequestMethod.DELETE)
    public void removeTemplateInternal(@PathVariable final String templateName) throws RestErrorException {
        log(Level.INFO, "[removeTemplateInternal] - removing template [" + templateName + "].");
        // check if the template is being used by at least one service, so it cannot be removed.
        final List<String> templateServices = getTemplateServices(templateName);
        if (!templateServices.isEmpty()) {
            log(Level.WARNING, "[removeTemplateInternal] - failed to remove template [" + templateName
                    + "]. The template is being used by the following services: " + templateServices);
            throw new RestErrorException(CloudifyErrorMessages.TEMPLATE_IN_USE.getName(), templateName,
                    templateServices);
        }
        // try to remove the template
        try {
            removeTemplateFromCloud(templateName);
        } catch (final RestErrorException e) {
            log(Level.WARNING, "[removeTemplateInternal] - failed to remove template [" + templateName + "].", e);
            throw e;
        }
        log(Level.INFO, "[removeTemplateInternal] - Successfully removed template [" + templateName + "].");
    }

    private void removeTemplateFromCloud(final String templateName) throws RestErrorException {
        log(Level.FINE, "[removeTemplateFromCloud] - removing template [" + templateName + "] from cloud.");
        // delete template's file from the cloud configuration directory.
        try {
            deleteTemplateFile(templateName);
        } catch (final RestErrorException e) {
            log(Level.WARNING,
                    "[removeTemplateFromCloud] - failed to remove template's files: " + e.getLocalizedMessage()
                            + ". The template will not be removed from the cloud's tempaltes list.");
            throw e;
        }
        // remove template from cloud's list
        removeTemplateFromCloudList(templateName);
    }

    private void removeTemplateFromCloudList(final String templateName) throws RestErrorException {
        log(Level.FINE,
                "[removeTemplateFromCloudList] - removing template [" + templateName + "] from cloud's list.");
        final Map<String, ComputeTemplate> cloudTemplates = cloud.getCloudCompute().getTemplates();
        if (!cloudTemplates.containsKey(templateName)) {
            log(Level.WARNING, "[removeTemplateFromCloudList] - tempalte [" + templateName
                    + "] doesn't exist in cloud's list.");
            throw new RestErrorException(CloudifyErrorMessages.TEMPLATE_NOT_EXIST.getName(), templateName);
        }
        cloudTemplates.remove(templateName);
        log(Level.FINE,
                "[removeTemplateFromCloudList] - template [" + templateName + "] was removed from cloud's list.");
    }

    /**
     * Deletes the template's file. Deletes the templates folder if no other templates files exist in the folder.
     * Deletes the {@link CloudifyConstants#ADDITIONAL_TEMPLATES_FOLDER_NAME} folder if empty.
     * 
     * @param templateName
     * @throws RestErrorException
     */
    private void deleteTemplateFile(final String templateName) throws RestErrorException {
        final File templateFolder = getTemplateFolder(templateName);
        if (templateFolder == null) {
            throw new RestErrorException(CloudifyErrorMessages.FAILED_REMOVE_TEMPLATE_FILE.getName(), templateName,
                    "failed to get template's folder");
        }
        final File templateFile = getTemplateFile(templateName, templateFolder);
        if (templateFile == null) {
            throw new RestErrorException(CloudifyErrorMessages.FAILED_REMOVE_TEMPLATE_FILE.getName(), templateName,
                    "template file doesn't exist");
        }
        // delete the file from the directory.
        final String templatesPath = templateFile.getAbsolutePath();
        log(Level.FINE, "[deleteTemplateFile] - removing template file " + templatesPath);

        boolean deleted = false;
        try {
            deleted = templateFile.delete();
        } catch (final SecurityException e) {
            log(Level.WARNING, "[deleteTemplateFile] - Failed to deleted template file " + templatesPath
                    + ", Error: " + e.getMessage(), e);
            throw new RestErrorException(CloudifyErrorMessages.FAILED_REMOVE_TEMPLATE_FILE.getName(), templatesPath,
                    "Security exception: " + e.getMessage());
        }
        if (!deleted) {
            throw new RestErrorException(CloudifyErrorMessages.FAILED_REMOVE_TEMPLATE_FILE.getName(), templatesPath,
                    "template file was not deleted.");
        }
        log(Level.FINE, "[deleteTemplateFile] - Successfully deleted template file [" + templatesPath + "].");
        // delete properties and overrides files if exist.
        ComputeTemplatesReader.removeTemplateFiles(templateFolder, templateName);
        deleteTemplateFolderIfNeeded(templateName, templateFolder);
    }

    private void deleteTemplateFolderIfNeeded(final String templateName, final File templateFolder) {
        log(Level.FINE, "[deleteTemplateFile] - checking if the folder of template [" + templateName
                + "] can be deleted [" + templateFolder + "].");
        final File[] templatesFiles = DSLReader.findDefaultDSLFiles(DSLUtils.TEMPLATE_DSL_FILE_NAME_SUFFIX,
                templateFolder);
        if (templatesFiles == null || templatesFiles.length == 0) {
            // no other templates files in this folder
            try {
                log(Level.FINE, "[deleteTemplateFile] - templates folder is empty, deleting it.");
                FileUtils.deleteDirectory(templateFolder);
            } catch (final IOException e) {
                log(Level.WARNING, "[deleteTemplateFile] - Failed to delete templates folder" + templateFolder, e);
            }
        } else {
            log(Level.FINE, "[deleteTemplateFile] - templates folder is not empty.");
        }
    }

    private File getTemplateFolder(final String templateName) {
        final ComputeTemplate computeTemplate = cloud.getCloudCompute().getTemplates().get(templateName);
        final String absoluteUploadDir = computeTemplate.getAbsoluteUploadDir();
        final File parentFile = new File(absoluteUploadDir).getParentFile();
        if (parentFile == null) {
            log(Level.WARNING, "Failed to get template's folder for template " + templateName
                    + ". The template's upload directory is " + absoluteUploadDir);
        }
        return parentFile;
    }

    /**
     * Searches for a file with file name templateName-template.groovy in the given folder.
     * 
     * @param templateName
     *            the name of the template (also the prefix of the wanted file).
     * @return the found file or null.
     */
    private File getTemplateFile(final String templateName, final File templateFolder) {
        final String templateFileName = templateName + DSLUtils.TEMPLATE_DSL_FILE_NAME_SUFFIX;
        log(Level.FINE,
                "Searching for template file " + templateFileName + " in " + templateFolder.getAbsolutePath());
        final File[] listFiles = templateFolder.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(final File dir, final String name) {
                return templateFileName.equals(name);
            }
        });
        final int length = listFiles.length;
        if (length == 0) {
            log(Level.WARNING, "Didn't find template file with name " + templateName + " at "
                    + templateFolder.getAbsolutePath());
            return null;
        }
        if (length > 1) {
            log(Level.WARNING, "Found " + length + " templates files with name " + templateName + ": "
                    + Arrays.toString(listFiles) + ". Returning the first one found.");
        }
        return listFiles[0];
    }

    /**
     * Returns the name of the protocol used for communication with the rest server. If the security is secure (SSL)
     * returns "https", otherwise returns "http".
     * 
     * @param isSecureConnection
     *            Indicates whether SSL is used or not.
     * @return "https" if this is a secure connection, "http" otherwise.
     */
    private static String getRestProtocol(final boolean isSecureConnection) {
        if (isSecureConnection) {
            return "https";
        }
        return "http";
    }

    private RestClientInternal createRestClientInternal(final String host, final String port)
            throws RestClientException {
        final String protocol = getRestProtocol(permissionEvaluator != null);
        final String baseUrl = protocol + "://" + IPUtils.getSafeIpAddress(host) + ":" + port;
        final String apiVersion = PlatformVersion.getVersion();
        try {
            return new RestClientInternal(new URL(baseUrl), "", "", apiVersion);
        } catch (final MalformedURLException e) {
            throw MessagesUtils.createRestClientException(ExceptionUtils.getFullStackTrace(e),
                    CloudifyErrorMessages.FAILED_CREATE_REST_CLIENT.getName(), ExceptionUtils.getFullStackTrace(e));
        }
    }

    private void log(final Level level, final String content) {
        if (logger.isLoggable(level)) {
            logger.log(level, content);
        }
    }

    private void log(final Level level, final String content, final Throwable thrown) {
        if (logger.isLoggable(level)) {
            logger.log(level, content, thrown);
        }
    }

}