org.apache.stratos.autoscaler.service.impl.AutoscalerServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.stratos.autoscaler.service.impl.AutoscalerServiceImpl.java

Source

/**
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.stratos.autoscaler.service.impl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.SynapseException;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
import org.apache.stratos.autoscaler.service.IAutoscalerService;
import org.apache.stratos.autoscaler.service.exception.AutoscalerServiceException;
import org.apache.stratos.autoscaler.service.exception.DeserializationException;
import org.apache.stratos.autoscaler.service.exception.SerializationException;
import org.apache.stratos.autoscaler.service.io.Deserializer;
import org.apache.stratos.autoscaler.service.io.Serializer;
import org.apache.stratos.autoscaler.service.jcloud.ComputeServiceBuilder;
import org.apache.stratos.autoscaler.service.util.AutoscalerConstant;
import org.apache.stratos.autoscaler.service.util.IaasContext;
import org.apache.stratos.autoscaler.service.util.IaasProvider;
import org.apache.stratos.autoscaler.service.util.InstanceContext;
import org.apache.stratos.autoscaler.service.util.ServiceTemplate;
import org.apache.stratos.autoscaler.service.xml.ElasticScalerConfigFileReader;
import org.apache.stratos.lb.common.conf.util.Constants;
import org.wso2.carbon.utils.CarbonUtils;

/**
 * Autoscaler Service is responsible for starting up new server instances, terminating already
 * started instances, providing pending instance count.
 * 
 */
public class AutoscalerServiceImpl implements IAutoscalerService {

    private static final Log log = LogFactory.getLog(AutoscalerServiceImpl.class);

    /**
     * pointer to Carbon Home directory.
     */
    private static final String CARBON_HOME = CarbonUtils.getCarbonHome();

    /**
     * pointer to Carbon Temp directory.
     */
    private static final String CARBON_TEMP = CarbonUtils.getTmpDir();

    /**
     * Tenant id Delimiter
     */
    private static final String TENANT_ID_DELIMITER = "/t/";

    /**
     * This directory will be used to store serialized objects.
     */
    private String serializationDir;

    /**
     * List of all <code>IaaSProviders</code> specified in the config file.
     */
    private List<IaasProvider> iaasProviders;

    /**
     * List of all <code>ServiceTemplate</code> objects.
     */
    private List<ServiceTemplate> serviceTemps;

    /**
     * We keep an enum which contains all supported IaaSes.
     */
    public enum Iaases {
        ec2, openstack
    };

    /**
     * List which keeps <code>IaasContext</code> objects.
     */
    private List<IaasContext> iaasContextList = new ArrayList<IaasContext>();

    /**
     * We keep track of the IaaS where the last instance of a domain and a sub domain combination is spawned.
     * This is required when there are multiple <code>IaasProvider</code>s defined.
     * Key - domain
     * Value - a map which has a Key - sub domain and Value - name of the {@link IaasProvider}.
     */
    private Map<String, Map<String, String>> lastlyUsedIaasMap = new HashMap<String, Map<String, String>>();

    /**
     * To track whether the {@link #initAutoscaler(boolean)} method has been called.
     */
    boolean isInitialized = false;

    @Override
    public boolean initAutoscaler(boolean isSpi) {

        if (!isInitialized) {

            log.debug("InitAutoscaler has started ...  IsSPI : " + isSpi);

            // load configuration file
            ElasticScalerConfigFileReader configReader = new ElasticScalerConfigFileReader();

            // read serialization directory from config file if specified, else will use the
            // default.
            if ("".equals(serializationDir = configReader.getSerializationDir())) {
                serializationDir = CARBON_TEMP;

                log.debug("Directory to be used to serialize objects: " + serializationDir);
            }

            // let's deserialize and load the serialized objects.
            deserialize();

            // from config file, we grab the details unique to IaaS providers.
            iaasProviders = configReader.getIaasProvidersList();

            // from config file, we grab the details related to each service domain.
            serviceTemps = configReader.getTemplates();

            // we iterate through each IaaSProvider which is loaded from the config file.
            for (IaasProvider iaas : iaasProviders) {

                // build the JClouds specific ComputeService object
                ComputeService computeService = ComputeServiceBuilder.buildComputeService(iaas);
                IaasContext entity;

                // let's see whether there's a serialized entity
                entity = findIaasContext(iaas.getType());

                if (entity != null) {

                    log.debug("Serializable object is loaded for IaaS " + iaas.getType());

                    // ComputeService isn't serializable, hence we need to set it in the
                    // deserialized
                    // object.
                    entity.setComputeService(computeService);
                }

                // build JClouds Template objects according to different IaaSes
                if (iaas.getType().equalsIgnoreCase(Iaases.ec2.toString())) {

                    // initiate the IaasContext object, if it is null.
                    entity = (entity == null) ? (entity = new IaasContext(Iaases.ec2, computeService)) : entity;

                    // we should build the templates only if this is not SPI stuff
                    if (!isSpi) {
                        // Build the Template
                        buildEC2Templates(entity, iaas.getTemplate(), isSpi);

                    } else {
                        // add to data structure
                        iaasContextList.add(entity);
                    }

                } else if (iaas.getType().equalsIgnoreCase(Iaases.openstack.toString())) {

                    // initiate the IaasContext object, if it is null.
                    entity = (entity == null) ? (entity = new IaasContext(Iaases.openstack, computeService))
                            : entity;

                    // we should build the templates only if this is not SPI stuff
                    if (!isSpi) {
                        // Build the Template
                        buildLXCTemplates(entity, iaas.getTemplate(), isSpi, null);

                    } else {
                        // add to data structure
                        iaasContextList.add(entity);
                    }

                } else {
                    // unsupported IaaS detected. We only complain, since there could be other
                    // IaaSes.
                    String msg = "Unsupported IaasProvider is specified in the config file: " + iaas.getType()
                            + ". Supported IaasProviders are " + print(Iaases.values());
                    log.warn(msg);
                    continue;
                }

                // populate scale up order
                fillInScaleUpOrder();

                // populate scale down order
                fillInScaleDownOrder();

                // serialize the objects
                serialize();
            }

            // we couldn't locate any valid IaaS providers from config file, thus shouldn't proceed.
            if (iaasContextList.size() == 0) {
                String msg = "No valid IaaS provider specified in the config file!";
                log.error(msg);
                throw new AutoscalerServiceException(msg);
            }

            // initialization completed.
            isInitialized = true;

            log.info("Autoscaler service initialized successfully!!");

            return true;
        }

        log.debug("Autoscaler Service is already initialized!");
        return false;
    }

    @Override
    public boolean startInstance(String domainName, String subDomainName) {

        // initialize the service, if it's not already initialized.
        initAutoscaler(false);

        ComputeService computeService;
        Template template;

        subDomainName = checkSubDomain(subDomainName);

        log.info("Starting new instance of domain : " + domainName + " and sub domain : " + subDomainName);

        // sort the IaasContext entities according to scale up order
        Collections.sort(iaasContextList, IaasContextComparator
                .ascending(IaasContextComparator.getComparator(IaasContextComparator.SCALE_UP_SORT)));

        // traverse through IaasContext object instances in scale up order
        for (IaasContext iaasCtxt : iaasContextList) {

            // get the ComputeService
            computeService = iaasCtxt.getComputeService();

            // from the list grab the Template corresponds to this domain
            template = iaasCtxt.getTemplate(domainName, subDomainName);

            if (template == null) {
                String msg = "Failed to start an instance in " + iaasCtxt.getName().toString()
                        + ". Reason : Template is null. You have not specify a matching service "
                        + "element in the configuration file of Autoscaler.\n Hence, will try to "
                        + "start in another IaaS if available.";
                log.error(msg);
                continue;
            }

            // generate the group id from domain name and sub domain name.
            // Should have lower-case ASCII letters, numbers, or dashes.
            String str = domainName.concat("-" + subDomainName);
            String group = str.replaceAll("[^a-z0-9-]", "");

            try {
                // create and start a node
                Set<? extends NodeMetadata> nodes = computeService.createNodesInGroup(group, 1, template);

                NodeMetadata node = nodes.iterator().next();

                //                // add the details of the started node to maps
                //                iaasCtxt.addNodeIdToDomainMap(node.getId(), domainName);

                String ip = "";

                // set public Ip, if it's available
                if (node.getPublicAddresses().size() > 0) {
                    ip = node.getPublicAddresses().iterator().next();
                    //                    iaasCtxt.addPublicIpToDomainMap(publicIp, domainName);
                    //                    iaasCtxt.addPublicIpToNodeIdMap(publicIp, node.getId());
                } else if (node.getPrivateAddresses().size() > 0) { // set private IPs if no public IP s are returned
                    ip = node.getPrivateAddresses().iterator().next();
                    //                    iaasCtxt.addPublicIpToDomainMap(privateIp, domainName);
                    //                    iaasCtxt.addPublicIpToNodeIdMap(privateIp, node.getId());
                } else {
                    log.debug("Public IP of the node : " + node.getId() + " cannot be found.");
                }

                if (iaasCtxt.getInstanceContext(domainName, subDomainName) == null) {
                    String msg = "Failed to start an instance in " + iaasCtxt.getName().toString()
                            + ". Reason : InstanceContext is null. Hence, will try to start in another IaaS if available.";
                    log.error(msg);
                    continue;
                }

                if (node.getId() == null) {
                    String msg = "Node id of the starting instance is null.\n" + node.toString();
                    log.fatal(msg);
                    throw new AutoscalerServiceException(msg);
                }

                // add run time data to InstanceContext
                iaasCtxt.addNodeDetails(domainName, subDomainName, node.getId(), ip);

                // since we modified the IaasContext instance, let's replace it.
                replaceIaasContext(iaasCtxt);

                // update the lastlyUsedIaasMap
                addToLastlyUsedIaasMap(domainName, subDomainName, iaasCtxt.getName().toString());
                //                lastlyUsedIaasMap.put(domainName, iaasCtxt.getName().toString());

                if (log.isDebugEnabled()) {
                    log.debug("Node details: \n" + node.toString() + "\n***************\n");
                }

            } catch (RunNodesException e) {
                log.warn("Failed to start an instance in " + iaasCtxt.getName().toString()
                        + ". Hence, will try to start in another IaaS if available.", e);
                continue;
            }

            log.info("Instance is successfully starting up in IaaS " + iaasCtxt.getName().toString() + " ...");

            // serialize the objects
            serialize();

            return true;
        }

        log.info("Failed to start instance, in any available IaaS.");

        return false;

    }

    private String checkSubDomain(String subDomainName) {
        // if sub domain is null, we assume it as default one.
        if (subDomainName == null || "null".equalsIgnoreCase(subDomainName)) {
            subDomainName = Constants.DEFAULT_SUB_DOMAIN;
            log.debug("Sub domain is null, hence using the default value : " + subDomainName);
        }

        return subDomainName;
    }

    private void addToLastlyUsedIaasMap(String domainName, String subDomainName, String iaasName) {

        Map<String, String> map;

        if (lastlyUsedIaasMap.get(domainName) == null) {
            map = new HashMap<String, String>();

        } else {
            map = lastlyUsedIaasMap.get(domainName);
        }

        map.put(subDomainName, iaasName);
        lastlyUsedIaasMap.put(domainName, map);

    }

    @Override
    public String startSpiInstance(String domainName, String subDomainName, String imageId) {

        log.debug("Starting an SPI instance ... | domain: " + domainName + " | sub domain: " + subDomainName
                + " | ImageId: " + imageId);

        // initialize the service, if it's not already initialized.
        initAutoscaler(true);

        String tenantId = null;
        String spiDomainName = null;

        if (domainName != null) {
            // domainName will have the pattern <domainName>/t/<tenantId>
            String[] arr = domainName.split(TENANT_ID_DELIMITER);
            if (arr.length != 2) {
                String msg = "Domain name does not match with the expected pattern. Expected "
                        + "pattern is <domainName>/t/<tenantId>";
                log.error(msg);
                throw new AutoscalerServiceException(msg);
            }
            spiDomainName = arr[0];
            tenantId = arr[1];
        }

        IaasContext entry;

        // FIXME: Build the Templates, for now we're doing a hack here. I don't know whether
        // there's a proper fix.
        // handle openstack case
        if (imageId.startsWith("nova") && ((entry = findIaasContext(Iaases.openstack)) != null)) {

            buildLXCTemplates(entry, imageId, true, tenantId);

        } else if (((entry = findIaasContext(Iaases.ec2)) != null)) {

            buildEC2Templates(entry, imageId, true);

        } else {
            String msg = "Invalid image id: " + imageId;
            log.error(msg);
            throw new AutoscalerServiceException(msg);
        }

        // let's start the instance
        if (startInstance(spiDomainName, subDomainName)) {

            // if it's successful, get the public IP of the started instance.
            // FIXME remove --> String publicIP =
            // findIaasContext(iaas).getLastMatchingPublicIp(domainName);
            String publicIP = entry.getLastMatchingPublicIp(spiDomainName, subDomainName);

            // if public IP is null, return an empty string, else return public IP.
            return (publicIP == null) ? "" : publicIP;

        }

        return "";

    }

    @Override
    public boolean terminateInstance(String domainName, String subDomainName) {

        // initialize the service, if it's not already initialized.
        initAutoscaler(false);

        subDomainName = checkSubDomain(subDomainName);

        log.info("Starting to terminate an instance of domain : " + domainName + " and sub domain : "
                + subDomainName);

        // sort the IaasContext entities according to scale down order.
        Collections.sort(iaasContextList, IaasContextComparator
                .ascending(IaasContextComparator.getComparator(IaasContextComparator.SCALE_DOWN_SORT)));

        // traverse in scale down order
        for (IaasContext iaasTemp : iaasContextList) {

            String msg = "Failed to terminate an instance in " + iaasTemp.getName().toString()
                    + ". Hence, will try to terminate an instance in another IaaS if possible.";

            String nodeId = null;

            // grab the node ids related to the given domain and traverse
            for (String id : iaasTemp.getNodeIds(domainName, subDomainName)) {
                if (id != null) {
                    nodeId = id;
                    break;
                }
            }

            // if no matching node id can be found.
            if (nodeId == null) {

                log.warn(msg + " : Reason- No matching instance found for domain: " + domainName
                        + " and sub domain: " + subDomainName + ".");
                continue;
            }

            // terminate it!
            terminate(iaasTemp, nodeId);

            return true;

        }

        log.info("Termination of an instance which is belong to domain '" + domainName + "' and sub domain '"
                + subDomainName + "' , failed!\n Reason: No matching "
                + "running instance found in any available IaaS.");

        return false;

    }

    @Override
    public boolean terminateLastlySpawnedInstance(String domainName, String subDomainName) {

        // initialize the service, if it's not already initialized.
        initAutoscaler(false);

        subDomainName = checkSubDomain(subDomainName);

        // see whether there is a matching IaaS, where we spawn an instance belongs to given domain.
        if (lastlyUsedIaasMap.containsKey(domainName)) {

            // grab the name of the IaaS
            String iaas = lastlyUsedIaasMap.get(domainName).get(subDomainName);

            // find the corresponding IaasContext
            IaasContext iaasTemp = findIaasContext(iaas);

            String msg = "Failed to terminate the lastly spawned instance of '" + domainName + "' service domain.";

            if (iaasTemp == null) {
                log.error(msg + " : Reason- Iaas' data cannot be located!");
                return false;
            }

            // find the instance spawned at last of this IaasContext
            String nodeId = iaasTemp.getLastMatchingNode(domainName, subDomainName);

            if (nodeId == null) {
                log.error(msg + " : Reason- No matching instance found for domain: " + domainName
                        + " and sub domain: " + subDomainName + ".");
                return false;
            }

            // terminate it!
            terminate(iaasTemp, nodeId);

            return true;

        }

        log.info("Termination of an instance which is belong to domain '" + domainName + "' and sub domain '"
                + subDomainName + "' , failed!\n Reason: No matching instance found.");

        return false;
    }

    @Override
    public boolean terminateSpiInstance(String publicIp) {

        // initialize the service, if it's not already initialized.
        initAutoscaler(true);

        // sort the IaasContext entities according to scale down order.
        Collections.sort(iaasContextList, IaasContextComparator
                .ascending(IaasContextComparator.getComparator(IaasContextComparator.SCALE_DOWN_SORT)));

        // traverse in scale down order
        for (IaasContext iaasTemp : iaasContextList) {

            String msg = "Failed to terminate an instance in " + iaasTemp.getName().toString() + ""
                    + ". Hence, will try to terminate an instance in another IaaS if possible.";

            // grab the node maps with the given public IP address
            String nodeId = iaasTemp.getNodeWithPublicIp(publicIp);

            if (nodeId == null) {
                log.warn(msg + " : Reason- No matching instance found for public ip '" + publicIp + "'.");
                continue;
            }

            // terminate it!
            terminate(iaasTemp, nodeId);

            return true;
        }

        log.info("Termination of an instance which has the public IP '" + publicIp + "', failed!");

        return false;
    }

    @Override
    public int getPendingInstanceCount(String domainName, String subDomain) {

        // initialize the service, if it's not already initialized.
        initAutoscaler(false);

        subDomain = checkSubDomain(subDomain);

        int pendingInstanceCount = 0;

        // traverse through IaasContexts
        for (IaasContext entry : iaasContextList) {

            ComputeService computeService = entry.getComputeService();

            // get list of node Ids which are belong to the requested domain
            List<String> nodeIds = entry.getNodeIds(domainName, subDomain);

            if (nodeIds.isEmpty()) {
                log.debug("Zero nodes spawned in the IaaS " + entry.getName() + " of domain: " + domainName
                        + " and sub domain: " + subDomain);
                continue;
            }

            // get all the nodes spawned by this IaasContext
            Set<? extends ComputeMetadata> set = computeService.listNodes();

            Iterator<? extends ComputeMetadata> iterator = set.iterator();

            // traverse through all nodes of this ComputeService object
            while (iterator.hasNext()) {
                NodeMetadataImpl nodeMetadata = (NodeMetadataImpl) iterator.next();

                // if this node belongs to the requested domain
                if (nodeIds.contains(nodeMetadata.getId())) {

                    // get the status of the node
                    Status nodeStatus = nodeMetadata.getStatus();

                    // count nodes that are in pending state
                    if (nodeStatus.toString().equalsIgnoreCase("PENDING")) {
                        pendingInstanceCount++;
                    }
                }

            }
        }

        log.info("Pending instance count of domain '" + domainName + "' and sub domain '" + subDomain + "' is "
                + pendingInstanceCount);

        return pendingInstanceCount;

    }

    /**
     * Returns matching IaasContext for the given {@link Iaases} entry.
     */
    private IaasContext findIaasContext(Enum<Iaases> iaas) {

        for (IaasContext entry : iaasContextList) {
            if (entry.getName().equals(iaas)) {
                return entry;
            }
        }

        return null;
    }

    /**
     * Returns matching IaasContext for the given iaas type.
     */
    private IaasContext findIaasContext(String iaasType) {

        for (IaasContext entry : iaasContextList) {
            if (entry.getName().toString().equals(iaasType)) {
                return entry;
            }
        }

        return null;
    }

    private void fillInScaleDownOrder() {

        for (IaasProvider iaas : iaasProviders) {
            if (findIaasContext(iaas.getType()) != null) {
                findIaasContext(iaas.getType()).setScaleDownOrder(iaas.getScaleDownOrder());
            }
        }

    }

    private void fillInScaleUpOrder() {

        for (IaasProvider iaas : iaasProviders) {
            if (findIaasContext(iaas.getType()) != null) {
                findIaasContext(iaas.getType()).setScaleUpOrder(iaas.getScaleUpOrder());
            }
        }

    }

    private byte[] getUserData(String payloadFileName, String tenantId) {

        byte[] bytes = null;
        File outputFile = null;
        String tempfilename = UUID.randomUUID().toString();
        try {
            File file = new File(payloadFileName);
            if (!file.exists()) {
                handleException("Payload file " + payloadFileName + " does not exist");
            }
            if (!file.canRead()) {
                handleException("Payload file " + payloadFileName + " does cannot be read");
            }
            if (tenantId != null) {
                // Tenant Id is available. This is an spi scenario. Edit the payload content
                editPayload(tenantId, file, tempfilename);
                outputFile = new File(CARBON_HOME + File.separator + AutoscalerConstant.RESOURCES_DIR
                        + File.separator + tempfilename + ".zip");
            } else {
                outputFile = file;
            }
            bytes = getBytesFromFile(outputFile);

        } catch (IOException e) {
            handleException("Cannot read data from payload file " + payloadFileName, e);
        }

        // Remove temporary payload file
        if (tenantId != null) {
            outputFile.delete();
        }

        return bytes;
    }

    private void editPayload(String tenantName, File file, String tempfileName) {

        unzipFile(file, tempfileName);
        editContent(tenantName, file, tempfileName);
        zipPayloadFile(tempfileName);
    }

    /**
    * unzips the payload file
    * 
    * @param file
     * @param tempfileName 
    */
    private void unzipFile(File file, String tempfileName) {

        int buffer = 2048;
        BufferedOutputStream dest = null;
        ZipInputStream zis = null;

        try {
            FileInputStream fis = new FileInputStream(file);
            zis = new ZipInputStream(new BufferedInputStream(fis));
            ZipEntry entry;

            while ((entry = zis.getNextEntry()) != null) {

                log.debug("Extracting: " + entry);

                int count;
                byte data[] = new byte[buffer];
                String outputFilename = tempfileName + File.separator + entry.getName();
                createDirIfNeeded(tempfileName, entry);

                // write the files to the disk
                if (!entry.isDirectory()) {
                    FileOutputStream fos = new FileOutputStream(outputFilename);
                    dest = new BufferedOutputStream(fos, buffer);
                    while ((count = zis.read(data, 0, buffer)) != -1) {
                        dest.write(data, 0, count);
                    }
                    dest.flush();
                    dest.close();
                }
            }

        } catch (Exception e) {
            log.error("Exception is occurred in unzipping payload file. Reason:" + e.getMessage());
            throw new AutoscalerServiceException(e.getMessage(), e);
        } finally {
            closeStream(zis);
            closeStream(dest);
        }
    }

    private void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                log.error(" Exception is occurred when closing stream. Reason :" + e.getMessage());
            }
        }
    }

    /**
     * 
     * Modify contents (tenantName) of the debian_cron_script.sh file
     * 
     * @param tenantName
     * @param file
     */
    private void editContent(String tenantName, File file, String tempfileName) {

        File f = new File(CARBON_HOME + File.separator + tempfileName + File.separator
                + AutoscalerConstant.PAYLOAD_DIR + File.separator + AutoscalerConstant.PARAMS_FILE_NAME);

        FileInputStream fs = null;
        InputStreamReader in = null;
        BufferedReader br = null;

        StringBuffer sb = new StringBuffer();

        String textinLine;

        try {
            fs = new FileInputStream(f);
            in = new InputStreamReader(fs);
            br = new BufferedReader(in);

            while (true) {

                textinLine = br.readLine();
                if (textinLine == null)
                    break;
                sb.append(editLine(textinLine, tenantName));
            }
        } catch (Exception e) {
            log.error("Exception is occurred in editing payload content. Reason: " + e.getMessage());
            throw new AutoscalerServiceException(e.getMessage(), e);
        } finally {
            closeStream(fs);
            closeStream(in);
            closeStream(br);
        }

        writeChangesBackToFile(f, sb);
    }

    private String editLine(String textinLine, String tenantName) {

        // Format of the line will be <IP>=<IPvalue>,<Path>=<PathValue>..

        StringBuffer outputBuffer = new StringBuffer();
        Map<String, String> paramMap = new HashMap<String, String>();
        String[] params = textinLine.split(AutoscalerConstant.ENTRY_SEPARATOR);

        for (int i = 0; i < params.length; i++) {

            // split the params one by one
            String param = params[i];
            String[] values = param.split(AutoscalerConstant.VALUE_SEPARATOR);

            if (values.length != 2) {
                throw new AutoscalerServiceException("Incorrect format in parameters file");
            }

            String key = values[0];
            String value = values[1];

            String updatedValue = value;

            if (AutoscalerConstant.TENANT_KEY.equals(key)) {
                updatedValue = tenantName;
            } else if (AutoscalerConstant.APP_PATH_KEY.equals(key)) {
                updatedValue = getAppPathForTenant(tenantName, value);
            }
            paramMap.put(key, updatedValue);
        }

        // Loop through the map and put values into a string
        reOrganizeContent(outputBuffer, paramMap);

        // cleanup output buffer
        if (outputBuffer.substring(0, 1).equals(AutoscalerConstant.ENTRY_SEPARATOR)) {
            outputBuffer.delete(0, 1);
        }

        return outputBuffer.toString();
    }

    private void reOrganizeContent(StringBuffer outputBuffer, Map<String, String> paramMap) {

        for (Map.Entry<String, String> entry : paramMap.entrySet()) {
            outputBuffer.append(AutoscalerConstant.ENTRY_SEPARATOR).append(entry.getKey())
                    .append(AutoscalerConstant.VALUE_SEPARATOR).append(entry.getValue());
        }
    }

    private String getAppPathForTenant(String tenantName, String appPath) {
        // Assumes app path is /opt/wso2-app/repository/
        StringBuffer updatedAppPath = new StringBuffer();
        if (tenantName.equals(AutoscalerConstant.SUPER_TENANT_ID)) {
            updatedAppPath.append(appPath).append("deployment").append(File.separator).append("server")
                    .append(File.separator).append("phpapps");
        } else {
            updatedAppPath.append(appPath).append(tenantName).append(File.separator).append("phpapps");
        }
        return updatedAppPath.toString();
    }

    private void writeChangesBackToFile(File f, StringBuffer sb) {
        FileWriter fstream = null;
        BufferedWriter outobj = null;

        try {
            fstream = new FileWriter(f);
            outobj = new BufferedWriter(fstream);
            outobj.write(sb.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            closeStream(outobj);
        }
    }

    /**
     * 
     * Compress the modified files back into payload.zip
     * @param tempfileName 
     * 
     */
    private void zipPayloadFile(String tempfileName) {

        int buffer = 2048;
        BufferedInputStream origin = null;
        ZipOutputStream out = null;

        try {

            FileOutputStream dest = new FileOutputStream(CARBON_HOME + File.separator
                    + AutoscalerConstant.RESOURCES_DIR + File.separator + tempfileName + ".zip");
            out = new ZipOutputStream(new BufferedOutputStream(dest));
            byte data[] = new byte[buffer];

            File f = new File(
                    CARBON_HOME + File.separator + tempfileName + File.separator + AutoscalerConstant.PAYLOAD_DIR);
            String files[] = f.list();

            for (int i = 0; i < files.length; i++) {
                FileInputStream fi = new FileInputStream(CARBON_HOME + File.separator + tempfileName
                        + File.separator + AutoscalerConstant.PAYLOAD_DIR + File.separator + files[i]);
                origin = new BufferedInputStream(fi, buffer);
                ZipEntry entry = new ZipEntry(AutoscalerConstant.PAYLOAD_DIR + File.separator + files[i]);
                out.putNextEntry(entry);

                int count;
                while ((count = origin.read(data, 0, buffer)) != -1) {
                    out.write(data, 0, count);
                }
                origin.close();
            }

            // delete temp files
            deleteDir(f);
            File fl = new File(CARBON_HOME + File.separator + tempfileName);
            fl.delete();

        } catch (Exception e) {
            log.error("Exception is occurred in zipping payload file after modification. Reason:" + e.getMessage());
            throw new AutoscalerServiceException(e.getMessage(), e);
        } finally {
            closeStream(origin);
            closeStream(out);
        }
    }

    private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }

        // The directory is now empty so delete it
        return dir.delete();
    }

    private void createDirIfNeeded(String destDirectory, ZipEntry entry) {

        String name = entry.getName();

        if (name.contains("/")) {
            log.debug("directory will need to be created");

            int index = name.lastIndexOf("/");
            String dirSequence = name.substring(0, index);

            File newDirs = new File(destDirectory + File.separator + dirSequence);

            //create the directory
            newDirs.mkdirs();
        }

    }

    /**
      * Returns the contents of the file in a byte array
      * 
      * @param file
      *            - Input File
      * @return Bytes from the file
      * @throws java.io.IOException
      *             , if retrieving the file contents failed.
      */
    private byte[] getBytesFromFile(File file) throws IOException {
        if (!file.exists()) {
            log.error("Payload file " + file.getAbsolutePath() + " does not exist");
            return null;
        }
        InputStream is = new FileInputStream(file);
        byte[] bytes;

        try {
            // Get the size of the file
            long length = file.length();

            // You cannot create an array using a long type.
            // It needs to be an int type.
            // Before converting to an int type, check
            // to ensure that file is not larger than Integer.MAX_VALUE.
            if (length > Integer.MAX_VALUE) {
                if (log.isDebugEnabled()) {
                    log.debug("File is too large");
                }
            }

            // Create the byte array to hold the data
            bytes = new byte[(int) length];

            // Read in the bytes
            int offset = 0;
            int numRead;
            while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }

            // Ensure all the bytes have been read in
            if (offset < bytes.length) {
                throw new IOException("Could not completely read file " + file.getName());
            }
        } finally {
            // Close the input stream and return bytes
            is.close();
        }

        return bytes;
    }

    /**
     * handles the exception
     * 
     * @param msg
     *            exception message
     */
    private void handleException(String msg) {
        log.error(msg);
        throw new SynapseException(msg);
    }

    /**
     * handles the exception
     * 
     * @param msg
     *            exception message
     * @param e
     *            exception
     */
    private static void handleException(String msg, Exception e) {
        log.error(msg, e);
        throw new SynapseException(msg, e);
    }

    /**
     * This will replace an existing entry in iaasEntities list, if there's such.
     * If not this will add the replacement value to the list.
     * 
     * @param replacement
     *            IaasContext entry to be added.
     */
    private void replaceIaasContext(IaasContext replacement) {
        for (IaasContext entry : iaasContextList) {
            if (entry.equals(replacement)) {
                int idx = iaasContextList.indexOf(entry);
                iaasContextList.remove(idx);
                iaasContextList.add(idx, replacement);
                return;
            }
        }
        iaasContextList.add(replacement);

    }

    /**
     * Builds the LXC Template object.
     */
    private void buildLXCTemplates(IaasContext entity, String imageId, boolean blockUntilRunning, String tenantId) {

        if (entity.getComputeService() == null) {
            throw new AutoscalerServiceException("Compute service is null for IaaS provider: " + entity.getName());
        }

        //        // if domain to template map is null
        //        if (entity.getDomainToTemplateMap() == null) {
        //            // we initialize it
        //            entity.setDomainToTemplateMap(new HashMap<String, Template>());
        //        }

        TemplateBuilder templateBuilder = entity.getComputeService().templateBuilder();
        templateBuilder.imageId(imageId);

        // to avoid creation of template objects in each and every time, we create all
        // at once!
        for (ServiceTemplate temp : serviceTemps) {

            String instanceType;

            // set instance type 
            if (((instanceType = temp.getProperty("instanceType." + Iaases.openstack.toString())) != null)) {

                templateBuilder.hardwareId(instanceType);
            }

            Template template = templateBuilder.build();

            template.getOptions().as(TemplateOptions.class).blockUntilRunning(blockUntilRunning);

            template.getOptions().as(NovaTemplateOptions.class).securityGroupNames(
                    temp.getProperty("securityGroups").split(AutoscalerConstant.ENTRY_SEPARATOR));

            if (temp.getProperty(AutoscalerConstant.PAYLOAD_DIR) != null) {
                template.getOptions().as(NovaTemplateOptions.class).userData(getUserData(
                        CARBON_HOME + File.separator + temp.getProperty(AutoscalerConstant.PAYLOAD_DIR), tenantId));
            }

            template.getOptions().as(NovaTemplateOptions.class).keyPairName(temp.getProperty("keyPair"));

            // add to the data structure
            if (entity.getInstanceContext(temp.getDomainName(), temp.getSubDomainName()) == null) {
                entity.addInstanceContext(
                        new InstanceContext(temp.getDomainName(), temp.getSubDomainName(), template));
            } else {
                entity.getInstanceContext(temp.getDomainName(), temp.getSubDomainName()).setTemplate(template);
            }

        }

        // since we modified the Context, we need to replace
        replaceIaasContext(entity);
    }

    /**
     * Builds EC2 Template object
     * 
     */
    private void buildEC2Templates(IaasContext entity, String imageId, boolean blockUntilRunning) {

        if (entity.getComputeService() == null) {
            throw new AutoscalerServiceException("Compute service is null for IaaS provider: " + entity.getName());
        }

        //        // if domain to template map is null
        //        if (entity.getDomainToTemplateMap() == null) {
        //            // we initialize it
        //            entity.setDomainToTemplateMap(new HashMap<String, Template>());
        //        }

        TemplateBuilder templateBuilder = entity.getComputeService().templateBuilder();

        // set image id specified
        templateBuilder.imageId(imageId);

        // to avoid creation of template objects in each and every time, we create all
        // at once! FIXME we could use caching and lazy loading
        for (ServiceTemplate temp : serviceTemps) {

            if (temp.getProperty("instanceType." + Iaases.ec2.toString()) != null) {
                // set instance type eg: m1.large
                templateBuilder.hardwareId(temp.getProperty("instanceType." + Iaases.ec2.toString()));
            }

            // build the Template
            Template template = templateBuilder.build();

            // make it non blocking
            template.getOptions().as(TemplateOptions.class).blockUntilRunning(blockUntilRunning);

            // set EC2 specific options
            template.getOptions().as(AWSEC2TemplateOptions.class)
                    .placementGroup(temp.getProperty("availabilityZone"));

            template.getOptions().as(AWSEC2TemplateOptions.class)
                    .securityGroups(temp.getProperty("securityGroups").split(AutoscalerConstant.ENTRY_SEPARATOR));

            if (temp.getProperty(AutoscalerConstant.PAYLOAD_DIR) != null) {
                template.getOptions().as(AWSEC2TemplateOptions.class).userData(getUserData(
                        CARBON_HOME + File.separator + temp.getProperty(AutoscalerConstant.PAYLOAD_DIR), null));
            }

            template.getOptions().as(AWSEC2TemplateOptions.class).keyPair(temp.getProperty("keyPair"));

            // add to the data structure
            if (entity.getInstanceContext(temp.getDomainName(), temp.getSubDomainName()) == null) {
                entity.addInstanceContext(
                        new InstanceContext(temp.getDomainName(), temp.getSubDomainName(), template));
            } else {
                entity.getInstanceContext(temp.getDomainName(), temp.getSubDomainName()).setTemplate(template);
            }

        }

        // since we modified the Context, we need to replace
        replaceIaasContext(entity);

    }

    private String print(Iaases[] values) {
        String str = "";
        for (Iaases iaases : values) {
            str = iaases.name() + ", ";
        }
        str = str.trim();
        return str.endsWith(AutoscalerConstant.ENTRY_SEPARATOR) ? str.substring(0, str.length() - 1) : str;
    }

    @SuppressWarnings("unchecked")
    private void deserialize() {

        String path;

        try {
            path = serializationDir + File.separator + AutoscalerConstant.IAAS_CONTEXT_LIST_SERIALIZING_FILE;

            Object obj = Deserializer.deserialize(path);
            if (obj != null) {
                iaasContextList = (List<IaasContext>) obj;
                log.debug("Deserialization was successful from file: " + path);
            }

            path = serializationDir + File.separator + AutoscalerConstant.LASTLY_USED_IAAS_MAP_SERIALIZING_FILE;

            obj = Deserializer.deserialize(path);

            if (obj != null) {
                lastlyUsedIaasMap = (Map<String, Map<String, String>>) obj;
                log.debug("Deserialization was successful from file: " + path);
            }

        } catch (Exception e) {
            String msg = "Deserialization of objects failed!";
            log.fatal(msg, e);
            throw new DeserializationException(msg, e);
        }

    }

    /**
     * Does all the serialization stuff!
     */
    private void serialize() {

        try {
            Serializer.serialize(iaasContextList,
                    serializationDir + File.separator + AutoscalerConstant.IAAS_CONTEXT_LIST_SERIALIZING_FILE);

            Serializer.serialize(lastlyUsedIaasMap,
                    serializationDir + File.separator + AutoscalerConstant.LASTLY_USED_IAAS_MAP_SERIALIZING_FILE);

        } catch (IOException e) {
            String msg = "Serialization of objects failed!";
            log.fatal(msg, e);
            throw new SerializationException(msg, e);
        }
    }

    /**
     * A helper method to terminate an instance.
     */
    private void terminate(IaasContext iaasTemp, String nodeId) {

        // this is just to be safe
        if (iaasTemp.getComputeService() == null) {
            String msg = "Unexpeced error occured! IaasContext's ComputeService is null!";
            log.error(msg);
            throw new AutoscalerServiceException(msg);
        }

        // destroy the node
        iaasTemp.getComputeService().destroyNode(nodeId);

        // remove the node id from the Context
        iaasTemp.removeNodeId(nodeId);

        // replace this IaasContext instance, as it reflects the new changes.
        replaceIaasContext(iaasTemp);

        // serialize the objects
        serialize();

        log.info("Node with Id: '" + nodeId + "' is terminated!");
    }

    /**
     * Comparator to compare IaasContexts on different attributes.
     */
    public enum IaasContextComparator implements Comparator<IaasContext> {
        SCALE_UP_SORT {
            public int compare(IaasContext o1, IaasContext o2) {
                return Integer.valueOf(o1.getScaleUpOrder()).compareTo(o2.getScaleUpOrder());
            }
        },
        SCALE_DOWN_SORT {
            public int compare(IaasContext o1, IaasContext o2) {
                return Integer.valueOf(o1.getScaleDownOrder()).compareTo(o2.getScaleDownOrder());
            }
        };

        public static Comparator<IaasContext> ascending(final Comparator<IaasContext> other) {
            return new Comparator<IaasContext>() {
                public int compare(IaasContext o1, IaasContext o2) {
                    return other.compare(o1, o2);
                }
            };
        }

        public static Comparator<IaasContext> getComparator(final IaasContextComparator... multipleOptions) {
            return new Comparator<IaasContext>() {
                public int compare(IaasContext o1, IaasContext o2) {
                    for (IaasContextComparator option : multipleOptions) {
                        int result = option.compare(o1, o2);
                        if (result != 0) {
                            return result;
                        }
                    }
                    return 0;
                }
            };
        }
    }

}