org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess.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.zeppelin.interpreter.remote;

import com.google.gson.Gson;
import org.apache.commons.exec.*;
import org.apache.commons.exec.environment.EnvironmentUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.thrift.TException;
import org.apache.zeppelin.interpreter.InterpreterException;
import org.apache.zeppelin.interpreter.InterpreterGroup;
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 */
public class RemoteInterpreterProcess implements ExecuteResultHandler {
    private static final Logger logger = LoggerFactory.getLogger(RemoteInterpreterProcess.class);

    private final AtomicInteger referenceCount;
    private DefaultExecutor executor;
    private ExecuteWatchdog watchdog;
    boolean running = false;
    private int port = -1;
    private final String interpreterRunner;
    private final String interpreterDir;

    private GenericObjectPool<Client> clientPool;
    private Map<String, String> env;
    private final RemoteInterpreterEventPoller remoteInterpreterEventPoller;
    private final InterpreterContextRunnerPool interpreterContextRunnerPool;
    private int connectTimeout;

    public RemoteInterpreterProcess(String intpRunner, String intpDir, Map<String, String> env,
            int connectTimeout) {
        this(intpRunner, intpDir, env, new RemoteInterpreterEventPoller(), connectTimeout);
    }

    RemoteInterpreterProcess(String intpRunner, String intpDir, Map<String, String> env,
            RemoteInterpreterEventPoller remoteInterpreterEventPoller, int connectTimeout) {
        this.interpreterRunner = intpRunner;
        this.interpreterDir = intpDir;
        this.env = env;
        this.interpreterContextRunnerPool = new InterpreterContextRunnerPool();
        referenceCount = new AtomicInteger(0);
        this.remoteInterpreterEventPoller = remoteInterpreterEventPoller;
        this.connectTimeout = connectTimeout;
    }

    public int getPort() {
        return port;
    }

    public int reference(InterpreterGroup interpreterGroup) {
        synchronized (referenceCount) {
            if (executor == null) {
                // start server process
                try {
                    port = RemoteInterpreterUtils.findRandomAvailablePortOnAllLocalInterfaces();
                } catch (IOException e1) {
                    throw new InterpreterException(e1);
                }

                CommandLine cmdLine = CommandLine.parse(interpreterRunner);
                cmdLine.addArgument("-d", false);
                cmdLine.addArgument(interpreterDir, false);
                cmdLine.addArgument("-p", false);
                cmdLine.addArgument(Integer.toString(port), false);

                executor = new DefaultExecutor();

                watchdog = new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT);
                executor.setWatchdog(watchdog);

                running = true;
                try {
                    Map procEnv = EnvironmentUtils.getProcEnvironment();
                    procEnv.putAll(env);

                    logger.info("Run interpreter process {}", cmdLine);
                    executor.execute(cmdLine, procEnv, this);
                } catch (IOException e) {
                    running = false;
                    throw new InterpreterException(e);
                }

                long startTime = System.currentTimeMillis();
                while (System.currentTimeMillis() - startTime < connectTimeout) {
                    if (RemoteInterpreterUtils.checkIfRemoteEndpointAccessible("localhost", port)) {
                        break;
                    } else {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                        }
                    }
                }

                clientPool = new GenericObjectPool<Client>(new ClientFactory("localhost", port));

                remoteInterpreterEventPoller.setInterpreterGroup(interpreterGroup);
                remoteInterpreterEventPoller.setInterpreterProcess(this);
                remoteInterpreterEventPoller.start();
            }
            return referenceCount.incrementAndGet();
        }
    }

    public Client getClient() throws Exception {
        return clientPool.borrowObject();
    }

    public void releaseClient(Client client) {
        clientPool.returnObject(client);
    }

    public int dereference() {
        synchronized (referenceCount) {
            int r = referenceCount.decrementAndGet();
            if (r == 0) {
                logger.info("shutdown interpreter process");
                remoteInterpreterEventPoller.shutdown();

                // first try shutdown
                Client client = null;
                try {
                    client = getClient();
                    client.shutdown();
                } catch (Exception e) {
                    // safely ignore exception while client.shutdown() may terminates remote process
                } finally {
                    if (client != null) {
                        releaseClient(client);
                    }
                }

                clientPool.clear();
                clientPool.close();

                // wait for some time (connectTimeout) and force kill
                // remote process server.serve() loop is not always finishing gracefully
                long startTime = System.currentTimeMillis();
                while (System.currentTimeMillis() - startTime < connectTimeout) {
                    if (this.isRunning()) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                        }
                    } else {
                        break;
                    }
                }

                if (isRunning()) {
                    logger.info("kill interpreter process");
                    watchdog.destroyProcess();
                }

                executor = null;
                watchdog = null;
                running = false;
                logger.info("Remote process terminated");
            }
            return r;
        }
    }

    public int referenceCount() {
        synchronized (referenceCount) {
            return referenceCount.get();
        }
    }

    @Override
    public void onProcessComplete(int exitValue) {
        logger.info("Interpreter process exited {}", exitValue);
        running = false;

    }

    @Override
    public void onProcessFailed(ExecuteException e) {
        logger.info("Interpreter process failed {}", e);
        running = false;
    }

    public boolean isRunning() {
        return running;
    }

    public int getNumActiveClient() {
        if (clientPool == null) {
            return 0;
        } else {
            return clientPool.getNumActive();
        }
    }

    public int getNumIdleClient() {
        if (clientPool == null) {
            return 0;
        } else {
            return clientPool.getNumIdle();
        }
    }

    /**
     * Called when angular object is updated in client side to propagate
     * change to the remote process
     * @param name
     * @param o
     */
    public void updateRemoteAngularObject(String name, String noteId, Object o) {
        Client client = null;
        try {
            client = getClient();
        } catch (NullPointerException e) {
            // remote process not started
            return;
        } catch (Exception e) {
            logger.error("Can't update angular object", e);
        }

        try {
            Gson gson = new Gson();
            client.angularObjectUpdate(name, noteId, gson.toJson(o));
        } catch (TException e) {
            logger.error("Can't update angular object", e);
        } finally {
            releaseClient(client);
        }
    }

    public InterpreterContextRunnerPool getInterpreterContextRunnerPool() {
        return interpreterContextRunnerPool;
    }
}