ComputeNode.java Source code

Java tutorial

Introduction

Here is the source code for ComputeNode.java

Source

/**
 * @description Implements the Compute node interface
 * 
 * @authors Daniel William DaCosta, Bala Subrahmanyam Kambala
 * @license GPLv3 (http://www.gnu.org/copyleft/gpl.html)
 */

import java.rmi.*;
import java.rmi.server.*;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.Collections;
import java.util.Iterator;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.Exception;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.OptionBuilder;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;

public class ComputeNode extends UnicastRemoteObject implements ComputeNodeInterface {

    private static String defaultconf = "../cfg/default.config";

    private static Logger lg;

    private ServerInterface server;

    private Integer id;

    private Double underLoadThreshold;

    private Double overLoadThreshold;

    private Double failProbability;

    /**
     * Used to specify constant load.
     */
    private Double loadConstant;

    /**
     * Used in generating random values with gaussian distribution.
     */
    private Pair<Double, Double> loadGaussian;

    /**
     * Stores node stats
     */
    private NodeStats myNodeStats;

    /**
     * Used in terminating the node only during sorting task
     */
    private Boolean isExecutingSortTask = false;

    public ComputeNode(String servername, Double _underLoadThreshold, Double _overLoadThreshold,
            Double _failProbability, Double _loadConstant, Pair<Double, Double> _loadGaussian, String configFile)
            throws Exception {

        server = (ServerInterface) Naming.lookup("//" + servername + "/Server");

        id = server.registerNode();

        lg = new Logger("Compute Node:" + id);
        lg.log(Level.INFO, "ComputeNode " + id + " started.");

        // If a config file was specified
        if (configFile == null) {
            configFile = defaultconf;
        }

        try {
            // TODO: Only load if we need to.
            Properties properties = new Properties();
            properties.load(new FileInputStream(configFile));

            if (_overLoadThreshold == null)
                _overLoadThreshold = new Double(properties.getProperty("computenode.overload_threshhold"));
            overLoadThreshold = _overLoadThreshold;

            if (_underLoadThreshold == null)
                _underLoadThreshold = new Double(properties.getProperty("computenode.underload_threshhold"));
            underLoadThreshold = _underLoadThreshold;

            if (_failProbability == null)
                _failProbability = new Double(properties.getProperty("computenode.fail_probability"));
            failProbability = _failProbability;

            lg.log(Level.FINER, "ComputeNode " + id + ": under load threshhold = " + underLoadThreshold);
            lg.log(Level.FINER, "ComputeNode " + id + ": over load threshhold = " + overLoadThreshold);
            lg.log(Level.FINER, "ComputeNode " + id + ": fail probability = " + failProbability);

            loadConstant = _loadConstant;
            if (loadConstant != null)
                lg.log(Level.FINER, "ComputeNode " + id + ": load constant = " + loadConstant);

            loadGaussian = _loadGaussian;
            if (loadGaussian != null)
                lg.log(Level.FINER,
                        "ComputeNode " + id + ": load gaussian = " + loadGaussian.fst() + "," + loadGaussian.snd());

        } catch (Exception e) {
            lg.log(Level.SEVERE, "ComputeNode " + id + ": Constructor failure! " + underLoadThreshold);
            e.printStackTrace();
            System.exit(1);
        }

        myNodeStats = new NodeStats();
        myNodeStats.setCurrentLoad(getCurrentLoad());
    }

    public Integer getID() {
        return id;
    }

    @Override
    public void executeTask(Task task) throws RemoteException {
        //
        // If node couldn't transfer or load is less than overLoadThreshold, 
        // spawns worker thread.
        //
        Thread t = new TaskExecutor(task);
        t.start();

    }

    private class HeartBeatHandler extends TimerTask {
        public void run() {
            lg.log(Level.FINEST, " HeartBeatHandler.run: Enter");

            try {
                //
                // Sends heartbeat message if probability is greater than fail 
                // probability or not executing sort task or
                //
                if (!isExecutingSortTask || getProbability() > failProbability) {
                    lg.log(Level.FINEST, " HeartBeatHandler.run: Alive.");

                    server.heartBeatMsg(id);
                } else {
                    // Turning off node.
                    lg.log(Level.WARNING, " **HeartBeatHandler.run: DEAD!(exit)");
                    System.exit(0);
                }
            } catch (RemoteException e) {
                lg.log(Level.SEVERE, " **Unable to connect to server" + e.getMessage());
            }
            lg.log(Level.FINEST, " HeartBeatHandler.run: Exit");
        }
    }

    public class TaskExecutor extends Thread {
        Task myTask;

        public TaskExecutor(Task t) {
            myTask = t;
        }

        public void run() {
            try {
                Double load = getCurrentLoad();

                // If load is over the threshold
                if (load > overLoadThreshold) {

                    // Get active nodes
                    List<Pair<Integer, String>> computeNodes = server.getActiveNodes();

                    // Ask each node whether it can takes your load?
                    for (Integer i = 0; i < computeNodes.size(); i++) {
                        if (computeNodes.get(i).fst() != id) {
                            ComputeNodeInterface c;
                            String url = "//" + computeNodes.get(i).snd() + "/ComputeNode"
                                    + computeNodes.get(i).fst();
                            try {
                                c = (ComputeNodeInterface) Naming.lookup(url);

                                lg.log(Level.FINEST,
                                        "Requesting node " + computeNodes.get(i).fst() + " for task transfer");

                                myNodeStats.noOfTransferRequests.incrementAndGet();

                                // Requesting node to take up the task
                                Boolean isAccepted = c.taskTransferRequest(myTask);

                                // If transfer request is accepted, update
                                // server
                                if (isAccepted) {
                                    lg.log(Level.INFO, "Node " + computeNodes.get(i).fst()
                                            + " accepted the task transfer request");

                                    myNodeStats.getNoOfMigratedJobs().incrementAndGet();
                                    return;
                                }
                            } catch (ConnectException e) {
                                lg.log(Level.SEVERE, "Node with  " + "with url = " + url + "is not responding");

                            } catch (MalformedURLException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            } catch (NotBoundException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

            if (myTask.getTaskType().equals(Task.TaskType.MAP)) {
                sort((MapTask) myTask);
            } else {
                merge((ReduceTask) myTask);
            }
        }
    }

    /**
     * map task
     */
    private void sort(MapTask t) {

        lg.log(Level.INFO, "sort: Enter - " + t.getTaskId());

        t.setStartTaskTime(System.currentTimeMillis());

        // This is to make sure compute node fails only during sorting.
        isExecutingSortTask = true;

        myNodeStats.getNoOfJobs().incrementAndGet();
        try {
            Thread.sleep(2 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Iterator<Integer> iterator = t.getData().iterator();
        while (iterator.hasNext()) {
            lg.log(Level.FINER, "sort: Received integer -> " + iterator.next());
        }
        try {
            Collections.sort(t.getData());
            synchronized (t) {
                t.wait(10 * t.getData().size());
            }
            server.aggregateMapTasks(t);
        } catch (Exception e) {
            lg.log(Level.SEVERE, "Sort:Failure");
            e.printStackTrace();
            System.exit(1);
        }
        lg.log(Level.SEVERE, "sort: Exit - " + t.getTaskId());

        t.setEndTaskTime(System.currentTimeMillis());

        // This is to make sure compute node fails only during sorting.
        isExecutingSortTask = false;
    }

    /**
     * reduce task
     */
    private void merge(ReduceTask t) {
        lg.log(Level.INFO, "merge: Enter - " + t.getTaskId());
        List<MapTask> list = t.getData();
        List<Integer> rv = new ArrayList<Integer>();
        // TODO: Merge all the lists!

        try {
            // XXX: This algorithm for merging is brain-dead.
            // It could be much more efficient!
            for (; list.size() > 0;) {
                Integer mini = null;
                int i = 0;

                for (; i < list.size(); i++) {

                    if (mini == null || list.get(i).getData().get(0) < list.get(mini).getData().get(0)) {
                        mini = i;
                    }
                }

                rv.add(list.get(mini).getData().get(0));
                // XXX : remove must behave sensibly here and decrement the 
                // size
                list.get(mini).getData().remove(0);
                if (list.get(mini).getData().size() == 0) {
                    if (list.remove((int) mini) == null)
                        lg.log(Level.SEVERE, "merge: remove returned null!");
                }
            }
            MapTask mt = new MapTask();
            mt.setData(rv);
            t.getData().clear();
            t.getData().add(mt);
            server.aggregateReduceTasks(t);
        } catch (Exception e) {
            lg.log(Level.SEVERE, "Merge: Failure");
            e.printStackTrace();
            System.exit(1);
        }
        lg.log(Level.INFO, "merge: Exit - " + t.getTaskId());
    }

    /**
     * This method generates random number. Then this number will be checked 
     * against failed probability.
     */
    private Double getProbability() {
        Double random = Math.random() * 100;
        return random;
    }

    /**
     * Internal method to get load
     */
    private Double getCurrentLoad() {
        // can be a simulated load or command output
        // can use taskCount

        Double currLoad = 0d;
        try {
            Process p = Runtime.getRuntime().exec("uptime");

            BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

            BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

            // read the output from the command

            // 11:57:57 up 29 min,  2 users,  load average: 0.27, 0.12, 0.09

            String s;
            //System.out.println("Here is the standard output of the command:\n");

            s = stdInput.readLine();
            s = s.replace(':', ',');

            String data[] = s.split(",");

            currLoad = Double.parseDouble(data[data.length - 2]);

        } catch (Exception e) {
            lg.log(Level.WARNING,
                    "getCurrentLoad: Unable to parse system " + " load information! Using constant,guassian or 0 ");

        }
        if (loadConstant != null) {
            currLoad = loadConstant;
        } else {
            if (loadGaussian != null) {
                currLoad = RandomGaussian.getGaussian(loadGaussian.fst(), loadGaussian.snd());

            } else {
                currLoad = 0.0;
            }
        }

        myNodeStats.setCurrentLoad(currLoad);
        myNodeStats.noOfLoadChecks.incrementAndGet();
        myNodeStats.setTotalLoad(myNodeStats.getTotalLoad() + currLoad);

        lg.log(Level.FINER, " getCurrentLoad: Load " + currLoad);
        return currLoad;
    }

    @Override
    public Boolean taskTransferRequest(Task task) throws RemoteException {

        Double load = getCurrentLoad();
        myNodeStats.getNoOfTransferRequests().incrementAndGet();

        lg.log(Level.FINER, "currLoad :" + load + " expectedLoad: " + task.getExpectedLoad()
                + " overLoadThreshold :" + overLoadThreshold);

        if (load + task.getExpectedLoad() > overLoadThreshold) {
            return false;
        }

        try {
            InetAddress ownIP = InetAddress.getLocalHost();
            String localIP = ownIP.getHostAddress();
            task.setNode(new Pair(id, localIP));

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        server.updateTaskTransfer(task);
        Thread t = new TaskExecutor(task);
        t.start();

        // spawn task request...
        return true;
    }

    @Override
    public String getNodeStats() throws RemoteException {
        StringBuffer buf = new StringBuffer();
        buf.append("Current load: " + myNodeStats.getCurrentLoad());
        buf.append("\nAverage load: " + myNodeStats.getAverageLoad());
        buf.append("\nNo of completed tasks: " + myNodeStats.getNoOfJobs());
        buf.append("\nNo of transferred tasks: " + myNodeStats.getNoOfMigratedJobs());
        buf.append("\nNo of transferred requests made: " + myNodeStats.getNoOfTransferRequests());

        return buf.toString();
    }

    public static void main(String[] argv) {

        String fileservername = "localhost";
        String id = null;
        Double underLoad = null;
        Double overLoad = null;
        Double failProb = null;
        Double constantload = null;
        Pair<Double, Double> gaussian = null;
        String fileName = null;

        ArgumentHandler cli = new ArgumentHandler(
                "FileServer [-h] [collector address] [-u underload] "
                        + "[-o overload] [-c constant_load|-g mean variance] " + "[-p fail_prob] [-f configfile]",
                "Bala Subrahmanyam Kambala, Daniel William DaCosta - "
                        + "GPLv3 (http://www.gnu.org/copyleft/gpl.html)",
                "");
        cli.addOption("h", "help", false, "Print this usage information.");
        cli.addOption("u", "underLoad", true, "Under load threshold");
        cli.addOption("o", "overLoad", true, "Over load threshold");
        cli.addOption("c", "constant", true, "Generate constant load");
        cli.addOption("p", "probability", true, "Fail Probability(0-100)");
        cli.addOption("f", "configfile", true,
                "The configuration file to read parameters from. " + "The default is " + defaultconf + ". "
                        + "Command line arguments will override config file " + "arguments.");
        cli.addOption(OptionBuilder.withLongOpt("gaussian").hasArgs(2)
                .withDescription("Generate a gaussian probability model for load "
                        + "simulation. The first parameter is the mean "
                        + "and the second parameter is the variance.")
                .create('g'));

        // parse command line
        CommandLine commandLine = cli.parse(argv);
        if (commandLine.hasOption('h')) {
            cli.usage("");
            System.exit(0);
        }

        if (commandLine.hasOption('u')) {
            // TODO : Ensure the number is within range
            underLoad = Double.parseDouble(commandLine.getOptionValue('u'));
        }

        if (commandLine.hasOption('o')) {
            // TODO : Ensure the number is within range
            overLoad = Double.parseDouble(commandLine.getOptionValue('o'));
        }

        if (commandLine.hasOption('p')) {
            // TODO : Ensure the number is within range
            failProb = Double.parseDouble(commandLine.getOptionValue('p'));
        }

        if (commandLine.hasOption('c')) {
            // TODO : Ensure the number is within range
            constantload = Double.parseDouble(commandLine.getOptionValue('c'));
        }

        if (commandLine.hasOption('g')) {
            // TODO : Ensure the number is within range
            gaussian = new Pair<Double, Double>(Double.parseDouble(commandLine.getOptionValues('g')[0]),
                    Double.parseDouble(commandLine.getOptionValues('g')[1]));
        }

        // TODO: If these flags are no longer mutually exclusive this
        // code should be adjusted to account for whatever constraint are
        // needed.
        if ((constantload != null) && (gaussian != null)) {
            cli.usage("-g -c switches are mutually exclusive!\n");
            System.exit(1);
        }

        if (commandLine.hasOption('f')) {
            fileName = commandLine.getOptionValue('f');
        }

        if (commandLine.getArgs().length != 0)
            fileservername = commandLine.getArgs()[0];
        System.out.println(argv);
        try {
            ComputeNode node = new ComputeNode(fileservername, underLoad, overLoad, failProb, constantload,
                    gaussian, fileName);

            Naming.rebind("ComputeNode" + Integer.toString(node.getID()), node);

            // Scheduling heart beat message handler
            Timer t = new Timer();
            HeartBeatHandler h = node.new HeartBeatHandler();
            t.schedule(h, 0, 1 * 1000);

        } catch (ConnectException ce) {
            //lg.log(Level.SEVERE, "Server is not alive");
            ce.printStackTrace();
        } catch (Exception e) {
            //lg.log(Level.SEVERE, "Exception in file server");
            e.printStackTrace();
        }
    }
}