in.cs654.chariot.ashva.AshvaProcessor.java Source code

Java tutorial

Introduction

Here is the source code for in.cs654.chariot.ashva.AshvaProcessor.java

Source

/*
 * Copyright (c) 2016 Abhilash Kumar and Saurav Kumar.
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package in.cs654.chariot.ashva;

import in.cs654.chariot.avro.BasicRequest;
import in.cs654.chariot.avro.BasicResponse;
import in.cs654.chariot.utils.*;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;

/**
 * This class contains the function process which handles the requests sent to this ashva server.
 */
public class AshvaProcessor {

    private static final Logger LOGGER = Logger.getLogger("Ashva Processor");
    public static final Long PROCESS_DEFAULT_TIMEOUT = 5000L; // milliseconds

    /**
     * This function handles all the requests routed to it. It matches the request against the following:
     * DEVICE_INSTALL : This requires the ashva server to install (pull the docker image corresponding to) the device
     * BECOME_PRASHTI2 : This required the ashva server to initiate it's prashti and zookeeper servers
     * @param request to handle
     * @return response of the request
     */
    public static BasicResponse process(BasicRequest request) {
        if (request.getFunctionName().equals(ReservedFunctions.DEVICE_INSTALL.toString())) {
            return handleDeviceInstallRequest(request);
        } else if (request.getFunctionName().equals(ReservedFunctions.BECOME_PRASHTI2.toString())) {
            return handleBecomePrashtiRequest(request);
        } else {
            return handleTaskProcessingRequest(request);
        }
    }

    /**
     * This function handles device installation request.
     * It gets the device id and name of the docker image from the request, and then runs docker pull image to
     * get the image from docker hub.
     * @param request consisting of (device id, dockerImage)
     * @return empty response as nothing useful can be returned
     */
    private static BasicResponse handleDeviceInstallRequest(BasicRequest request) {
        final String deviceId = request.getDeviceId();
        final String dockerImage = request.getExtraData().get("dockerImage");
        final String cmd = "docker pull " + dockerImage;
        try {
            Runtime.getRuntime().exec(cmd);
            Mongo.addDevice(new Device(deviceId, dockerImage));
            LOGGER.info("Pulled docker image " + dockerImage + " for device " + deviceId);
        } catch (IOException ignore) {
            LOGGER.severe("Error in installing device " + deviceId + " (" + dockerImage + ")");
        }
        return ResponseFactory.getEmptyResponse(request);
    }

    /**
     * This function handles request to become a prashti server. This requires running of prashti server
     * and zookeeper server. The jar containing the classes is assumed to be present in the directory
     * specified in `chariot.sh` in the project root.
     * @param request to start prashti and zookeeper servers
     * @return empty response
     */
    private static BasicResponse handleBecomePrashtiRequest(BasicRequest request) {
        try {
            boolean alreadyRunning = false;
            final String myIPAddr = CommonUtils.getIPAddress();
            final List<Prashti> onlinePrashtiList = D2Client.getOnlinePrashtiServers();
            for (Prashti prashti : onlinePrashtiList) {
                if (prashti.getIpAddr().equals(myIPAddr)) {
                    alreadyRunning = true;
                }
            }
            if (!alreadyRunning) {
                LOGGER.info("Starting Prashti and ZooKeeper Server");
                Runtime.getRuntime().exec("./chariot.sh");
            } else {
                LOGGER.info("Prashti and Zookeeper already running on this machine");
            }
        } catch (IOException ignore) {
            LOGGER.severe("Error in starting Prashti and Zookeeper servers");
        }
        return ResponseFactory.getEmptyResponse(request);
    }

    /**
     * This function takes in request, executes the request and returns the response.
     * The request is written into /tmp/<request_id>.req file. While running the docker container, /tmp dir is mounted
     * to /tmp of the container. This enables ease of data exchange. TODO encryption
     * Docker runs the request and puts the result into /tmp/<request_id>.res.
     * If the request specifies a timeout, it is picked up, else default timeout is set for the process to run
     * @param request containing function_name and other required information
     * @return response of the request
     */
    private static BasicResponse handleTaskProcessingRequest(BasicRequest request) {
        final String requestID = request.getRequestId();
        try {
            final byte[] serializedBytes = AvroUtils.requestToBytes(request);
            final FileOutputStream fos = new FileOutputStream("/tmp/" + requestID + ".req");
            fos.write(serializedBytes);
            fos.close();
            String timeoutString = request.getExtraData().get("chariot_timeout");
            Long timeout;
            if (timeoutString != null) {
                timeout = Long.parseLong(timeoutString);
            } else {
                timeout = PROCESS_DEFAULT_TIMEOUT;
            }

            final String dockerImage = Mongo.getDockerImage(request.getDeviceId());
            final String cmd = "docker run -v /tmp:/tmp " + dockerImage + " /bin/chariot " + requestID;
            final Process process = Runtime.getRuntime().exec(cmd);
            TimeoutProcess timeoutProcess = new TimeoutProcess(process);
            timeoutProcess.start();
            try {
                timeoutProcess.join(timeout); // milliseconds
                if (timeoutProcess.exit != null) {
                    File file = new File("/tmp/" + requestID + ".res");
                    byte[] bytes = FileUtils.readFileToByteArray(file);
                    LOGGER.info("Response generated for request " + requestID);
                    return AvroUtils.bytesToResponse(bytes);
                } else {
                    LOGGER.severe("Timeout in generating response for request " + requestID);
                    return ResponseFactory.getTimeoutErrorResponse(request);
                }
            } catch (InterruptedException ignore) {
                timeoutProcess.interrupt();
                Thread.currentThread().interrupt();
                LOGGER.severe("Error in generating response for request " + requestID);
                return ResponseFactory.getErrorResponse(request);
            } finally {
                process.destroy();
            }
        } catch (IOException ignore) {
            LOGGER.severe("Error in generating response for request: " + requestID);
            return ResponseFactory.getErrorResponse(request);
        }
    }
}