org.apache.reef.runtime.mesos.evaluator.REEFExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.reef.runtime.mesos.evaluator.REEFExecutor.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.reef.runtime.mesos.evaluator;

import com.google.protobuf.ByteString;
import org.apache.reef.runtime.common.files.REEFFileNames;
import org.apache.reef.runtime.mesos.evaluator.parameters.MesosExecutorId;
import org.apache.reef.runtime.mesos.util.EvaluatorControl;
import org.apache.reef.runtime.mesos.util.EvaluatorLaunch;
import org.apache.reef.runtime.mesos.util.EvaluatorRelease;
import org.apache.reef.runtime.mesos.util.MesosRemoteManager;
import org.apache.reef.tang.Injector;
import org.apache.reef.tang.JavaConfigurationBuilder;
import org.apache.reef.tang.Tang;
import org.apache.reef.tang.annotations.Parameter;
import org.apache.reef.tang.formats.CommandLine;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.mesos.Executor;
import org.apache.mesos.ExecutorDriver;
import org.apache.mesos.MesosExecutorDriver;
import org.apache.mesos.Protos.ExecutorInfo;
import org.apache.mesos.Protos.FrameworkInfo;
import org.apache.mesos.Protos.SlaveInfo;
import org.apache.mesos.Protos.Status;
import org.apache.mesos.Protos.TaskID;
import org.apache.mesos.Protos.TaskInfo;
import org.apache.mesos.Protos.TaskState;
import org.apache.mesos.Protos.TaskStatus;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

public final class REEFExecutor implements Executor {
    private final static Logger LOG = Logger.getLogger(REEFExecutor.class.getName());

    private final MesosExecutorDriver mesosExecutorDriver;
    private final MesosRemoteManager mesosRemoteManager;
    private final ExecutorService executorService;
    private final REEFFileNames fileNames;
    private final String mesosExecutorId;

    private Process evaluatorProcess;
    private Integer evaluatorProcessExitValue;

    @Inject
    REEFExecutor(final EvaluatorControlHandler evaluatorControlHandler, final MesosRemoteManager mesosRemoteManager,
            final REEFFileNames fileNames, final @Parameter(MesosExecutorId.class) String mesosExecutorId) {
        this.mesosRemoteManager = mesosRemoteManager;
        this.mesosRemoteManager.registerHandler(EvaluatorControl.class, evaluatorControlHandler);
        this.mesosExecutorDriver = new MesosExecutorDriver(this);
        this.executorService = Executors.newCachedThreadPool();
        this.fileNames = fileNames;
        this.mesosExecutorId = mesosExecutorId;
    }

    @Override
    public final void registered(final ExecutorDriver driver, final ExecutorInfo executorInfo,
            final FrameworkInfo frameworkInfo, final SlaveInfo slaveInfo) {
        LOG.log(Level.FINEST, "Executor registered. driver: {0} executorInfo: {1} frameworkInfo: {2} slaveInfo {3}",
                new Object[] { driver, executorInfo, frameworkInfo, slaveInfo });
    }

    @Override
    public final void reregistered(final ExecutorDriver driver, final SlaveInfo slaveInfo) {
        LOG.log(Level.FINEST, "Executor reregistered. driver: {0}", driver);
    }

    @Override
    public final void disconnected(final ExecutorDriver driver) {
        this.onRuntimeError();
    }

    /**
     * We assume a long-running Mesos Task that manages a REEF Evaluator process, leveraging Mesos Executor's interface.
     */
    @Override
    public final void launchTask(final ExecutorDriver driver, final TaskInfo task) {
        driver.sendStatusUpdate(
                TaskStatus.newBuilder().setTaskId(TaskID.newBuilder().setValue(this.mesosExecutorId).build())
                        .setState(TaskState.TASK_STARTING).setSlaveId(task.getSlaveId())
                        .setMessage(this.mesosRemoteManager.getMyIdentifier()).build());
    }

    @Override
    public final void killTask(final ExecutorDriver driver, final TaskID taskId) {
        this.onStop();
    }

    @Override
    public final void frameworkMessage(final ExecutorDriver driver, final byte[] data) {
        LOG.log(Level.FINEST, "Framework Messge. ExecutorDriver: {0}, data: {1}.", new Object[] { driver, data });
    }

    @Override
    public final void shutdown(final ExecutorDriver driver) {
        this.onStop();
    }

    @Override
    public final void error(final ExecutorDriver driver, final String message) {
        this.onRuntimeError();
    }

    /////////////////////////////////////////////////////////////////
    // HELPER METHODS

    private void onStart() {
        this.executorService.submit(new Thread() {
            public void run() {
                final Status status;
                status = mesosExecutorDriver.run();
                LOG.log(Level.INFO, "MesosExecutorDriver ended with status {0}", status);
            }
        });
    }

    private void onStop() {
        // Shutdown REEF Evaluator
        if (this.evaluatorProcess != null) {
            this.evaluatorProcess.destroy();
            mesosExecutorDriver.sendStatusUpdate(TaskStatus.newBuilder()
                    .setTaskId(TaskID.newBuilder().setValue(mesosExecutorId).build())
                    .setState(TaskState.TASK_FINISHED)
                    .setMessage("Evaluator Process exited with status " + String.valueOf(evaluatorProcessExitValue))
                    .build());
        } else {
            mesosExecutorDriver.sendStatusUpdate(TaskStatus.newBuilder()
                    .setTaskId(TaskID.newBuilder().setValue(mesosExecutorId).build())
                    .setState(TaskState.TASK_FINISHED).setData(ByteString.copyFromUtf8("eval_not_run")) // TODO: a hack to pass closeEvaluator test, replace this with a better interface
                    .setMessage("Evaluator Process exited with status " + String.valueOf(evaluatorProcessExitValue))
                    .build());
        }

        // Shutdown Mesos Executor
        this.executorService.shutdown();
        this.mesosExecutorDriver.stop();
    }

    private void onRuntimeError() {
        // Shutdown REEF Evaluator
        if (this.evaluatorProcess != null) {
            this.evaluatorProcess.destroy();
        }
        mesosExecutorDriver.sendStatusUpdate(TaskStatus.newBuilder()
                .setTaskId(TaskID.newBuilder().setValue(mesosExecutorId).build()).setState(TaskState.TASK_FAILED)
                .setMessage("Evaluator Process exited with status " + String.valueOf(evaluatorProcessExitValue))
                .build());

        // Shutdown Mesos Executor
        this.executorService.shutdown();
        this.mesosExecutorDriver.stop();
    }

    public final void onEvaluatorRelease(final EvaluatorRelease evaluatorRelease) {
        LOG.log(Level.INFO, "Release!!!! {0}", evaluatorRelease.toString());
        assert (evaluatorRelease.getIdentifier().toString().equals(this.mesosExecutorId));
        this.onStop();
    }

    public final void onEvaluatorLaunch(final EvaluatorLaunch evaluatorLaunch) {
        LOG.log(Level.INFO, "Launch!!!! {0}", evaluatorLaunch.toString());
        assert (evaluatorLaunch.getIdentifier().toString().equals(this.mesosExecutorId));
        final ExecutorService evaluatorLaunchExecutorService = Executors.newSingleThreadExecutor();
        evaluatorLaunchExecutorService.submit(new Thread() {
            public void run() {
                try {
                    final List<String> command = Arrays.asList(evaluatorLaunch.getCommand().toString().split(" "));
                    LOG.log(Level.INFO, "Command!!!! {0}", command);
                    final FileSystem fileSystem = FileSystem.get(new Configuration());
                    final Path hdfsFolder = new Path(fileSystem.getUri() + "/" + mesosExecutorId);
                    final File localFolder = new File(fileNames.getREEFFolderName(),
                            fileNames.getLocalFolderName());

                    FileUtil.copy(fileSystem, hdfsFolder, localFolder, true, new Configuration());

                    evaluatorProcess = new ProcessBuilder().command(command)
                            .redirectError(new File(fileNames.getEvaluatorStderrFileName()))
                            .redirectOutput(new File(fileNames.getEvaluatorStdoutFileName())).start();

                    evaluatorProcessExitValue = evaluatorProcess.waitFor();

                    fileSystem.close();
                } catch (IOException | InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        evaluatorLaunchExecutorService.shutdown();
    }

    public static org.apache.reef.tang.Configuration parseCommandLine(final String[] args) throws IOException {
        final JavaConfigurationBuilder confBuilder = Tang.Factory.getTang().newConfigurationBuilder();

        new CommandLine(confBuilder).registerShortNameOfClass(MesosExecutorId.class).processCommandLine(args);

        return confBuilder.build();
    }

    /**
     * The starting point of the executor.
     */
    public static void main(final String[] args) throws Exception {
        final Injector injector = Tang.Factory.getTang().newInjector(parseCommandLine(args));
        final REEFExecutor REEFExecutor = injector.getInstance(REEFExecutor.class);
        REEFExecutor.onStart();
    }
}