/******************************************************************************* * 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.deploy; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import org.apache.commons.io.FileUtils; import org.cloudifysource.domain.Service; import org.cloudifysource.dsl.internal.DSLUtils; import org.cloudifysource.dsl.internal.packaging.Packager; import org.cloudifysource.dsl.rest.request.InstallApplicationRequest; import org.cloudifysource.dsl.rest.request.InstallServiceRequest; import org.cloudifysource.dsl.utils.ServiceUtils; import org.cloudifysource.rest.controllers.DeploymentsController; import org.cloudifysource.rest.controllers.helpers.PropertiesOverridesMerger; import com.j_spaces.kernel.Environment; /** * A Runnable implementation that executes the deployment logic of an application. * * @author adaml * */ public class ApplicationDeployerRunnable implements Runnable { private static final int SERVICE_INSTANCE_STARTUP_TIMEOUT_MINUTES = 60; private static final java.util.logging.Logger logger = java.util.logging.Logger .getLogger(ApplicationDeployerRunnable.class.getName()); private final InstallApplicationRequest installApplicationRequest; private final File appFile; private final File appDir; private final String applicationName; private final List<Service> services; private final String deploymentID; private final File applicationPropertiesFile; private final File applicationOverridesFile; private DeploymentsController controller; /** * Constructor. * @param controller * installation requests are delegated to this controller. * @param request * the install application request. * @param result * the application compilation result. * @param services * the list of services. * @param overridesFile * application overrides file. */ public ApplicationDeployerRunnable(final ApplicationDeployerRequest request) { this.installApplicationRequest = request.getRequest(); this.appFile = request.getAppFile(); this.appDir = request.getAppDir(); this.applicationName = installApplicationRequest.getApplicationName(); this.services = request.getServices(); this.deploymentID = request.getDeploymentID(); this.applicationPropertiesFile = request.getAppPropertiesFile(); this.applicationOverridesFile = request.getAppOverridesFile(); this.controller = request.getController(); } @Override public void run() { logger.fine("Installing application " + applicationName + " with the following services: " + services); final boolean asyncInstallPossible = isAsyncInstallPossibleForApplication(); logger.info("Async install setting is " + asyncInstallPossible); try { installServices(asyncInstallPossible); } catch (final IOException e) { e.printStackTrace(); } } private void installServices(final boolean async) throws IOException { logger.info("Installing services for application: " + applicationName + ". Async install: " + async + ". Number of services: " + this.services.size()); for (final Service service : services) { final String serviceName = service.getName(); final String absolutePUName = ServiceUtils.getAbsolutePUName(applicationName, serviceName); logger.info("Installing service: " + absolutePUName); service.getCustomProperties().put("usmJarPath", Environment.getHomeDirectory() + "/lib/platform/usm"); boolean found = false; try { final File serviceDir = new File(appDir, serviceName); File servicePropertiesFile = DSLUtils.getPropertiesFile(serviceDir); // merge service properties with application properties and overrides files // merge into service's properties file PropertiesOverridesMerger merger = new PropertiesOverridesMerger( servicePropertiesFile, applicationPropertiesFile, servicePropertiesFile, applicationOverridesFile); merger.merge(); // Pack the folder and name it absolutePuName logger.fine("[installServices] packing absolutePUName"); final File packedFile = Packager.pack(service, serviceDir, absolutePUName, null /* additionalServiceFiles */); appFile.delete(); packedFile.deleteOnExit(); // Deployment will be done using the service's absolute PU name. final InstallServiceRequest installServiceReq = createInstallServiceRequest(); final ServiceApplicationDependentProperties serviceProps = new ServiceApplicationDependentProperties(); serviceProps.setDependsOn(service.getDependsOn()); controller.installServiceInternal( applicationName, serviceName, installServiceReq, deploymentID, serviceProps, service, packedFile); try { FileUtils.deleteDirectory(packedFile.getParentFile()); } catch (final IOException ioe) { // sometimes this delete fails. Not sure why. Maybe deploy // is async? logger.warning("Failed to delete temporary directory: " + packedFile.getParentFile()); } if (!async) { logger.info("Waiting for instance of service: " + serviceName + " of application: " + applicationName); final boolean instanceFound = controller .waitForServiceInstance(applicationName, serviceName, SERVICE_INSTANCE_STARTUP_TIMEOUT_MINUTES, TimeUnit.MINUTES); if (!instanceFound) { throw new TimeoutException( "Service " + serviceName + " of application " + applicationName + " was installed, but no instance of the service has started after " + SERVICE_INSTANCE_STARTUP_TIMEOUT_MINUTES + " minutes."); } logger.info("Found instance of: " + serviceName); } found = true; logger.fine("service " + service + " deployed."); } catch (final Exception e) { logger.log( Level.SEVERE, "Failed to install service: " + serviceName + " of application: " + applicationName + ". Application installation will halt. " + "Some services may already have started, and should be shutdown manually. Error was: " + e.getMessage(), e); return; } if (!found) { logger.severe("Failed to find an instance of service: " + serviceName + " while installing application " + applicationName + ". Application installation will stop. Some services may have been installed!"); return; } } FileUtils.deleteDirectory(appDir); } InstallServiceRequest createInstallServiceRequest() { final InstallServiceRequest installServiceReq = new InstallServiceRequest(); installServiceReq.setCloudOverridesUploadKey(installApplicationRequest.getCloudOverridesUploadKey()); installServiceReq.setCloudConfigurationUploadKey(installApplicationRequest.getCloudConfigurationUploadKey()); installServiceReq.setAuthGroups(installApplicationRequest.getAuthGroups()); installServiceReq.setDebugAll(installApplicationRequest.isDebugAll()); installServiceReq.setDebugEvents(installApplicationRequest.getDebugEvents()); installServiceReq.setDebugMode(installApplicationRequest.getDebugMode()); installServiceReq.setSelfHealing(installApplicationRequest.isSelfHealing()); installServiceReq.setServiceFileName(null); return installServiceReq; } /** * * @return true if all services have Lifecycle events. */ public boolean isAsyncInstallPossibleForApplication() { // check if all services are USM for (final Service service : this.services) { if (service.getLifecycle() == null) { return false; } } return true; } }