org.apache.zeppelin.interpreter.InterpreterSetting.java Source code

Java tutorial

Introduction

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

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.StringMap;
import org.apache.commons.io.FileUtils;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.dep.Dependency;
import org.apache.zeppelin.dep.DependencyResolver;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.AngularObjectRegistryListener;
import org.apache.zeppelin.helium.ApplicationEventListener;
import org.apache.zeppelin.interpreter.launcher.InterpreterLaunchContext;
import org.apache.zeppelin.interpreter.launcher.InterpreterLauncher;
import org.apache.zeppelin.interpreter.lifecycle.NullLifecycleManager;
import org.apache.zeppelin.interpreter.recovery.NullRecoveryStorage;
import org.apache.zeppelin.interpreter.recovery.RecoveryStorage;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreter;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener;
import org.apache.zeppelin.plugin.PluginManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import static org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETER_MAX_POOL_SIZE;
import static org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETER_OUTPUT_LIMIT;
import static org.apache.zeppelin.util.IdHashes.generateId;

/**
 * Represent one InterpreterSetting in the interpreter setting page
 */
public class InterpreterSetting {

    private static final Logger LOGGER = LoggerFactory.getLogger(InterpreterSetting.class);
    private static final String SHARED_PROCESS = "shared_process";
    private static final String SHARED_SESSION = "shared_session";
    private static final Map<String, Object> DEFAULT_EDITOR = ImmutableMap.of("language", (Object) "text",
            "editOnDblClick", false);

    private String id;
    private String name;
    // the original interpreter setting template name where it is created from
    private String group;

    //TODO(zjffdu) make the interpreter.json consistent with interpreter-setting.json
    /**
     * properties can be either Properties or Map<String, InterpreterProperty>
     * properties should be:
     * - Properties when Interpreter instances are saved to `conf/interpreter.json` file
     * - Map<String, InterpreterProperty> when Interpreters are registered
     * : this is needed after https://github.com/apache/zeppelin/pull/1145
     * which changed the way of getting default interpreter setting AKA interpreterSettingsRef
     * Note(mina): In order to simplify the implementation, I chose to change properties
     * from Properties to Object instead of creating new classes.
     */
    private Object properties = new Properties();

    private Status status;
    private String errorReason;

    @SerializedName("interpreterGroup")
    private List<InterpreterInfo> interpreterInfos;

    private List<Dependency> dependencies = new ArrayList<>();
    private InterpreterOption option = new InterpreterOption();

    @SerializedName("runner")
    private InterpreterRunner interpreterRunner;

    ///////////////////////////////////////////////////////////////////////////////////////////
    private transient InterpreterSettingManager interpreterSettingManager;
    private transient String interpreterDir;
    private final transient Map<String, ManagedInterpreterGroup> interpreterGroups = new ConcurrentHashMap<>();

    private final transient ReentrantReadWriteLock.ReadLock interpreterGroupReadLock;
    private final transient ReentrantReadWriteLock.WriteLock interpreterGroupWriteLock;

    private transient AngularObjectRegistryListener angularObjectRegistryListener;
    private transient RemoteInterpreterProcessListener remoteInterpreterProcessListener;
    private transient ApplicationEventListener appEventListener;
    private transient DependencyResolver dependencyResolver;

    private transient ZeppelinConfiguration conf = new ZeppelinConfiguration();

    // TODO(zjffdu) ShellScriptLauncher is the only launcher implemention for now. It could be other
    // launcher in future when we have other launcher implementation. e.g. third party launcher
    // service like livy
    private transient InterpreterLauncher launcher;
    private transient LifecycleManager lifecycleManager;
    private transient RecoveryStorage recoveryStorage;
    private transient RemoteInterpreterEventServer interpreterEventServer;
    ///////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Builder class for InterpreterSetting
     */
    public static class Builder {
        private InterpreterSetting interpreterSetting;

        public Builder() {
            this.interpreterSetting = new InterpreterSetting();
        }

        public Builder setId(String id) {
            interpreterSetting.id = id;
            return this;
        }

        public Builder setName(String name) {
            interpreterSetting.name = name;
            return this;
        }

        public Builder setGroup(String group) {
            interpreterSetting.group = group;
            return this;
        }

        public Builder setInterpreterInfos(List<InterpreterInfo> interpreterInfos) {
            interpreterSetting.interpreterInfos = interpreterInfos;
            return this;
        }

        public Builder setProperties(Object properties) {
            interpreterSetting.properties = properties;
            return this;
        }

        public Builder setOption(InterpreterOption option) {
            interpreterSetting.option = option;
            return this;
        }

        public Builder setInterpreterDir(String interpreterDir) {
            interpreterSetting.interpreterDir = interpreterDir;
            return this;
        }

        public Builder setRunner(InterpreterRunner runner) {
            interpreterSetting.interpreterRunner = runner;
            return this;
        }

        public Builder setDependencies(List<Dependency> dependencies) {
            interpreterSetting.dependencies = dependencies;
            return this;
        }

        public Builder setConf(ZeppelinConfiguration conf) {
            interpreterSetting.conf = conf;
            return this;
        }

        public Builder setDependencyResolver(DependencyResolver dependencyResolver) {
            interpreterSetting.dependencyResolver = dependencyResolver;
            return this;
        }

        public Builder setInterpreterRunner(InterpreterRunner runner) {
            interpreterSetting.interpreterRunner = runner;
            return this;
        }

        public Builder setIntepreterSettingManager(InterpreterSettingManager interpreterSettingManager) {
            interpreterSetting.interpreterSettingManager = interpreterSettingManager;
            return this;
        }

        public Builder setRemoteInterpreterEventServer(RemoteInterpreterEventServer interpreterEventServer) {
            interpreterSetting.interpreterEventServer = interpreterEventServer;
            return this;
        }

        public Builder setRemoteInterpreterProcessListener(
                RemoteInterpreterProcessListener remoteInterpreterProcessListener) {
            interpreterSetting.remoteInterpreterProcessListener = remoteInterpreterProcessListener;
            return this;
        }

        public Builder setAngularObjectRegistryListener(
                AngularObjectRegistryListener angularObjectRegistryListener) {
            interpreterSetting.angularObjectRegistryListener = angularObjectRegistryListener;
            return this;
        }

        public Builder setApplicationEventListener(ApplicationEventListener applicationEventListener) {
            interpreterSetting.appEventListener = applicationEventListener;
            return this;
        }

        public Builder setLifecycleManager(LifecycleManager lifecycleManager) {
            interpreterSetting.lifecycleManager = lifecycleManager;
            return this;
        }

        public Builder setRecoveryStorage(RecoveryStorage recoveryStorage) {
            interpreterSetting.recoveryStorage = recoveryStorage;
            return this;
        }

        public InterpreterSetting create() {
            // post processing
            interpreterSetting.postProcessing();
            return interpreterSetting;
        }
    }

    public InterpreterSetting() {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        this.id = generateId();
        interpreterGroupReadLock = lock.readLock();
        interpreterGroupWriteLock = lock.writeLock();
    }

    void postProcessing() {
        this.status = Status.READY;
        this.id = this.name;
        if (this.lifecycleManager == null) {
            this.lifecycleManager = new NullLifecycleManager(conf);
        }
        if (this.recoveryStorage == null) {
            try {
                this.recoveryStorage = new NullRecoveryStorage(conf, interpreterSettingManager);
            } catch (IOException e) {
                // ignore this exception as NullRecoveryStorage will do nothing.
            }
        }
    }

    /**
     * Create interpreter from InterpreterSettingTemplate
     *
     * @param o interpreterSetting from InterpreterSettingTemplate
     */
    public InterpreterSetting(InterpreterSetting o) {
        this();
        this.id = o.name;
        this.name = o.name;
        this.group = o.group;
        this.properties = convertInterpreterProperties((Map<String, DefaultInterpreterProperty>) o.getProperties());
        this.interpreterInfos = new ArrayList<>(o.getInterpreterInfos());
        this.option = InterpreterOption.fromInterpreterOption(o.getOption());
        this.dependencies = new ArrayList<>(o.getDependencies());
        this.interpreterDir = o.getInterpreterDir();
        this.interpreterRunner = o.getInterpreterRunner();
        this.conf = o.getConf();
    }

    private void createLauncher() throws IOException {
        this.launcher = PluginManager.get().loadInterpreterLauncher(getLauncherPlugin(), recoveryStorage);
    }

    public AngularObjectRegistryListener getAngularObjectRegistryListener() {
        return angularObjectRegistryListener;
    }

    public RemoteInterpreterProcessListener getRemoteInterpreterProcessListener() {
        return remoteInterpreterProcessListener;
    }

    public ApplicationEventListener getAppEventListener() {
        return appEventListener;
    }

    public DependencyResolver getDependencyResolver() {
        return dependencyResolver;
    }

    public InterpreterSettingManager getInterpreterSettingManager() {
        return interpreterSettingManager;
    }

    public InterpreterSetting setAngularObjectRegistryListener(
            AngularObjectRegistryListener angularObjectRegistryListener) {
        this.angularObjectRegistryListener = angularObjectRegistryListener;
        return this;
    }

    public InterpreterSetting setAppEventListener(ApplicationEventListener appEventListener) {
        this.appEventListener = appEventListener;
        return this;
    }

    public InterpreterSetting setRemoteInterpreterProcessListener(
            RemoteInterpreterProcessListener remoteInterpreterProcessListener) {
        this.remoteInterpreterProcessListener = remoteInterpreterProcessListener;
        return this;
    }

    public InterpreterSetting setDependencyResolver(DependencyResolver dependencyResolver) {
        this.dependencyResolver = dependencyResolver;
        return this;
    }

    public InterpreterSetting setInterpreterSettingManager(InterpreterSettingManager interpreterSettingManager) {
        this.interpreterSettingManager = interpreterSettingManager;
        return this;
    }

    public InterpreterSetting setLifecycleManager(LifecycleManager lifecycleManager) {
        this.lifecycleManager = lifecycleManager;
        return this;
    }

    public InterpreterSetting setRecoveryStorage(RecoveryStorage recoveryStorage) {
        this.recoveryStorage = recoveryStorage;
        return this;
    }

    public InterpreterSetting setInterpreterEventServer(RemoteInterpreterEventServer interpreterEventServer) {
        this.interpreterEventServer = interpreterEventServer;
        return this;
    }

    public RecoveryStorage getRecoveryStorage() {
        return recoveryStorage;
    }

    public LifecycleManager getLifecycleManager() {
        return lifecycleManager;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getGroup() {
        return group;
    }

    private String getInterpreterGroupId(String user, String noteId) {
        String key;
        if (option.isExistingProcess) {
            key = Constants.EXISTING_PROCESS;
        } else if (getOption().isProcess()) {
            key = (option.perUserIsolated() ? user : "") + "-" + (option.perNoteIsolated() ? noteId : "");
        } else {
            key = SHARED_PROCESS;
        }

        //TODO(zjffdu) we encode interpreter setting id into groupId, this is not a good design
        return id + "-" + key;
    }

    private String getInterpreterSessionId(String user, String noteId) {
        String key;
        if (option.isExistingProcess()) {
            key = Constants.EXISTING_PROCESS;
        } else if (option.perNoteScoped() && option.perUserScoped()) {
            key = user + ":" + noteId;
        } else if (option.perUserScoped()) {
            key = user;
        } else if (option.perNoteScoped()) {
            key = noteId;
        } else {
            key = SHARED_SESSION;
        }

        return key;
    }

    public ManagedInterpreterGroup getOrCreateInterpreterGroup(String user, String noteId) {
        String groupId = getInterpreterGroupId(user, noteId);
        try {
            interpreterGroupWriteLock.lock();
            if (!interpreterGroups.containsKey(groupId)) {
                LOGGER.info("Create InterpreterGroup with groupId: {} for user: {} and note: {}", groupId, user,
                        noteId);
                ManagedInterpreterGroup intpGroup = createInterpreterGroup(groupId);
                interpreterGroups.put(groupId, intpGroup);
            }
            return interpreterGroups.get(groupId);
        } finally {
            interpreterGroupWriteLock.unlock();
            ;
        }
    }

    void removeInterpreterGroup(String groupId) {
        try {
            interpreterGroupWriteLock.lock();
            this.interpreterGroups.remove(groupId);
        } finally {
            interpreterGroupWriteLock.unlock();
        }
    }

    public ManagedInterpreterGroup getInterpreterGroup(String user, String noteId) {
        String groupId = getInterpreterGroupId(user, noteId);
        try {
            interpreterGroupReadLock.lock();
            return interpreterGroups.get(groupId);
        } finally {
            interpreterGroupReadLock.unlock();
            ;
        }
    }

    ManagedInterpreterGroup getInterpreterGroup(String groupId) {
        return interpreterGroups.get(groupId);
    }

    public ArrayList<ManagedInterpreterGroup> getAllInterpreterGroups() {
        try {
            interpreterGroupReadLock.lock();
            return new ArrayList(interpreterGroups.values());
        } finally {
            interpreterGroupReadLock.unlock();
        }
    }

    Map<String, Object> getEditorFromSettingByClassName(String className) {
        for (InterpreterInfo intpInfo : interpreterInfos) {
            if (className.equals(intpInfo.getClassName())) {
                if (intpInfo.getEditor() == null) {
                    break;
                }
                return intpInfo.getEditor();
            }
        }
        return DEFAULT_EDITOR;
    }

    void closeInterpreters(String user, String noteId) {
        ManagedInterpreterGroup interpreterGroup = getInterpreterGroup(user, noteId);
        if (interpreterGroup != null) {
            String sessionId = getInterpreterSessionId(user, noteId);
            interpreterGroup.close(sessionId);
        }
    }

    public void close() {
        LOGGER.info("Close InterpreterSetting: " + name);
        List<Thread> closeThreads = interpreterGroups.values().stream()
                .map(g -> new Thread(g::close, name + "-close"))
                .peek(t -> t
                        .setUncaughtExceptionHandler((th, e) -> LOGGER.error("InterpreterSetting close error", e)))
                .peek(Thread::start).collect(Collectors.toList());
        interpreterGroups.clear();
        for (Thread t : closeThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                LOGGER.error("Can't wait InterpreterSetting close threads", e);
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    public void setProperties(Object object) {
        if (object instanceof StringMap) {
            StringMap<String> map = (StringMap) properties;
            Properties newProperties = new Properties();
            for (String key : map.keySet()) {
                newProperties.put(key, map.get(key));
            }
            this.properties = newProperties;
        } else {
            this.properties = object;
        }
    }

    public Object getProperties() {
        return properties;
    }

    @VisibleForTesting
    public void setProperty(String name, String value) {
        ((Map<String, InterpreterProperty>) properties).put(name, new InterpreterProperty(name, value));
    }

    // This method is supposed to be only called by InterpreterSetting
    // but not InterpreterSetting Template
    public Properties getJavaProperties() {
        Properties jProperties = new Properties();
        Map<String, InterpreterProperty> iProperties = (Map<String, InterpreterProperty>) properties;
        for (Map.Entry<String, InterpreterProperty> entry : iProperties.entrySet()) {
            if (entry.getValue().getValue() != null) {
                jProperties.setProperty(entry.getKey().trim(), entry.getValue().getValue().toString().trim());
            }
        }

        if (!jProperties.containsKey("zeppelin.interpreter.output.limit")) {
            jProperties.setProperty("zeppelin.interpreter.output.limit",
                    conf.getInt(ZEPPELIN_INTERPRETER_OUTPUT_LIMIT) + "");
        }

        if (!jProperties.containsKey("zeppelin.interpreter.max.poolsize")) {
            jProperties.setProperty("zeppelin.interpreter.max.poolsize",
                    conf.getInt(ZEPPELIN_INTERPRETER_MAX_POOL_SIZE) + "");
        }

        String interpreterLocalRepoPath = conf.getInterpreterLocalRepoPath();
        //TODO(zjffdu) change it to interpreterDir/{interpreter_name}
        jProperties.setProperty("zeppelin.interpreter.localRepo", interpreterLocalRepoPath + "/" + id);
        return jProperties;
    }

    public ZeppelinConfiguration getConf() {
        return conf;
    }

    public InterpreterSetting setConf(ZeppelinConfiguration conf) {
        this.conf = conf;
        return this;
    }

    public List<Dependency> getDependencies() {
        return dependencies;
    }

    public void setDependencies(List<Dependency> dependencies) {
        this.dependencies = dependencies;
        loadInterpreterDependencies();
    }

    public InterpreterOption getOption() {
        return option;
    }

    public void setOption(InterpreterOption option) {
        this.option = option;
    }

    public String getInterpreterDir() {
        return interpreterDir;
    }

    public void setInterpreterDir(String interpreterDir) {
        this.interpreterDir = interpreterDir;
    }

    public List<InterpreterInfo> getInterpreterInfos() {
        return interpreterInfos;
    }

    void appendDependencies(List<Dependency> dependencies) {
        for (Dependency dependency : dependencies) {
            if (!this.dependencies.contains(dependency)) {
                this.dependencies.add(dependency);
            }
        }
        loadInterpreterDependencies();
    }

    void setInterpreterOption(InterpreterOption interpreterOption) {
        this.option = interpreterOption;
    }

    public void setProperties(Properties p) {
        this.properties = p;
    }

    void setGroup(String group) {
        this.group = group;
    }

    void setName(String name) {
        this.name = name;
    }

    /***
     * Interpreter status
     */
    public enum Status {
        DOWNLOADING_DEPENDENCIES, ERROR, READY
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public String getErrorReason() {
        return errorReason;
    }

    public void setErrorReason(String errorReason) {
        this.errorReason = errorReason;
    }

    public void setInterpreterInfos(List<InterpreterInfo> interpreterInfos) {
        this.interpreterInfos = interpreterInfos;
    }

    public InterpreterRunner getInterpreterRunner() {
        return interpreterRunner;
    }

    public void setInterpreterRunner(InterpreterRunner interpreterRunner) {
        this.interpreterRunner = interpreterRunner;
    }

    public String getLauncherPlugin() {
        if (group.equals("spark")) {
            return "SparkInterpreterLauncher";
        } else {
            return "StandardInterpreterLauncher";
        }
    }

    public boolean isUserAuthorized(List<String> userAndRoles) {
        if (!option.permissionIsSet()) {
            return true;
        }
        Set<String> intersection = new HashSet<>(userAndRoles);
        intersection.retainAll(option.getOwners());
        return intersection.isEmpty();
    }

    //////////////////////////// IMPORTANT ////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    // This is the only place to create interpreters. For now we always create multiple interpreter
    // together (one session). We don't support to create single interpreter yet.
    List<Interpreter> createInterpreters(String user, String interpreterGroupId, String sessionId) {
        List<Interpreter> interpreters = new ArrayList<>();
        List<InterpreterInfo> interpreterInfos = getInterpreterInfos();
        Properties intpProperties = getJavaProperties();
        for (InterpreterInfo info : interpreterInfos) {
            Interpreter interpreter = new RemoteInterpreter(intpProperties, sessionId, info.getClassName(), user,
                    lifecycleManager);
            if (info.isDefaultInterpreter()) {
                interpreters.add(0, interpreter);
            } else {
                interpreters.add(interpreter);
            }
            LOGGER.info("Interpreter {} created for user: {}, sessionId: {}", interpreter.getClassName(), user,
                    sessionId);
        }

        // TODO(zjffdu) this kind of hardcode is ugly. For now SessionConfInterpreter is used
        // for livy, we could add new property in interpreter-setting.json when there's new interpreter
        // require SessionConfInterpreter
        if (group.equals("livy")) {
            interpreters.add(new SessionConfInterpreter(intpProperties, sessionId, interpreterGroupId, this));
        } else {
            interpreters.add(new ConfInterpreter(intpProperties, sessionId, interpreterGroupId, this));
        }
        return interpreters;
    }

    synchronized RemoteInterpreterProcess createInterpreterProcess(String interpreterGroupId, String userName,
            Properties properties) throws IOException {
        if (launcher == null) {
            createLauncher();
        }
        InterpreterLaunchContext launchContext = new InterpreterLaunchContext(properties, option, interpreterRunner,
                userName, interpreterGroupId, id, group, name, interpreterEventServer.getPort(),
                interpreterEventServer.getHost());
        RemoteInterpreterProcess process = (RemoteInterpreterProcess) launcher.launch(launchContext);
        recoveryStorage.onInterpreterClientStart(process);
        return process;
    }

    List<Interpreter> getOrCreateSession(String user, String noteId) {
        ManagedInterpreterGroup interpreterGroup = getOrCreateInterpreterGroup(user, noteId);
        Preconditions.checkNotNull(interpreterGroup, "No InterpreterGroup existed for user {}, " + "noteId {}",
                user, noteId);
        String sessionId = getInterpreterSessionId(user, noteId);
        return interpreterGroup.getOrCreateSession(user, sessionId);
    }

    public Interpreter getDefaultInterpreter(String user, String noteId) {
        return getOrCreateSession(user, noteId).get(0);
    }

    public Interpreter getInterpreter(String user, String noteId, String replName) {
        Preconditions.checkNotNull(noteId, "noteId should be not null");
        Preconditions.checkNotNull(replName, "replName should be not null");

        String className = getInterpreterClassFromInterpreterSetting(replName);
        if (className == null) {
            return null;
        }
        List<Interpreter> interpreters = getOrCreateSession(user, noteId);
        for (Interpreter interpreter : interpreters) {
            if (className.equals(interpreter.getClassName())) {
                return interpreter;
            }
        }
        return null;
    }

    private String getInterpreterClassFromInterpreterSetting(String replName) {
        Preconditions.checkNotNull(replName, "replName should be not null");

        for (InterpreterInfo info : interpreterInfos) {
            String infoName = info.getName();
            if (null != info.getName() && replName.equals(infoName)) {
                return info.getClassName();
            }
        }
        //TODO(zjffdu) It requires user can not create interpreter with name `conf`,
        // conf is a reserved word of interpreter name
        if (replName.equals("conf")) {
            if (group.equals("livy")) {
                return SessionConfInterpreter.class.getName();
            } else {
                return ConfInterpreter.class.getName();
            }
        }
        return null;
    }

    private ManagedInterpreterGroup createInterpreterGroup(String groupId) {
        AngularObjectRegistry angularObjectRegistry;
        ManagedInterpreterGroup interpreterGroup = new ManagedInterpreterGroup(groupId, this);
        angularObjectRegistry = new RemoteAngularObjectRegistry(groupId, angularObjectRegistryListener,
                interpreterGroup);
        interpreterGroup.setAngularObjectRegistry(angularObjectRegistry);
        return interpreterGroup;
    }

    /**
     * Throw exception when interpreter process has already launched
     *
     * @param interpreterGroupId
     * @param properties
     * @throws IOException
     */
    public void setInterpreterGroupProperties(String interpreterGroupId, Properties properties) throws IOException {
        ManagedInterpreterGroup interpreterGroup = this.interpreterGroups.get(interpreterGroupId);
        for (List<Interpreter> session : interpreterGroup.sessions.values()) {
            for (Interpreter intp : session) {
                if (!intp.getProperties().equals(properties)
                        && interpreterGroup.getRemoteInterpreterProcess() != null
                        && interpreterGroup.getRemoteInterpreterProcess().isRunning()) {
                    throw new IOException("Can not change interpreter properties when interpreter process "
                            + "has already been launched");
                }
                intp.setProperties(properties);
            }
        }
    }

    private void loadInterpreterDependencies() {
        setStatus(Status.DOWNLOADING_DEPENDENCIES);
        setErrorReason(null);
        Thread t = new Thread() {
            public void run() {
                try {
                    // dependencies to prevent library conflict
                    File localRepoDir = new File(conf.getInterpreterLocalRepoPath() + "/" + id);
                    if (localRepoDir.exists()) {
                        try {
                            FileUtils.forceDelete(localRepoDir);
                        } catch (FileNotFoundException e) {
                            LOGGER.info("A file that does not exist cannot be deleted, nothing to worry", e);
                        }
                    }

                    // load dependencies
                    List<Dependency> deps = getDependencies();
                    if (deps != null) {
                        for (Dependency d : deps) {
                            File destDir = new File(
                                    conf.getRelativeDir(ZeppelinConfiguration.ConfVars.ZEPPELIN_DEP_LOCALREPO));

                            if (d.getExclusions() != null) {
                                dependencyResolver.load(d.getGroupArtifactVersion(), d.getExclusions(),
                                        new File(destDir, id));
                            } else {
                                dependencyResolver.load(d.getGroupArtifactVersion(), new File(destDir, id));
                            }
                        }
                    }

                    setStatus(Status.READY);
                    setErrorReason(null);
                } catch (Exception e) {
                    LOGGER.error(
                            String.format("Error while downloading repos for interpreter group : %s,"
                                    + " go to interpreter setting page click on edit and save it again to make "
                                    + "this interpreter work properly. : %s", getGroup(), e.getLocalizedMessage()),
                            e);
                    setErrorReason(e.getLocalizedMessage());
                    setStatus(Status.ERROR);
                }
            }
        };

        t.start();
    }

    //TODO(zjffdu) ugly code, should not use JsonObject as parameter. not readable
    public void convertPermissionsFromUsersToOwners(JsonObject jsonObject) {
        if (jsonObject != null) {
            JsonObject option = jsonObject.getAsJsonObject("option");
            if (option != null) {
                JsonArray users = option.getAsJsonArray("users");
                if (users != null) {
                    if (this.option.getOwners() == null) {
                        this.option.owners = new LinkedList<>();
                    }
                    for (JsonElement user : users) {
                        this.option.getOwners().add(user.getAsString());
                    }
                }
            }
        }
    }

    // For backward compatibility of interpreter.json format after ZEPPELIN-2403
    static Map<String, InterpreterProperty> convertInterpreterProperties(Object properties) {
        if (properties != null && properties instanceof StringMap) {
            Map<String, InterpreterProperty> newProperties = new HashMap<>();
            StringMap p = (StringMap) properties;
            for (Object o : p.entrySet()) {
                Map.Entry entry = (Map.Entry) o;
                if (!(entry.getValue() instanceof StringMap)) {
                    InterpreterProperty newProperty = new InterpreterProperty(entry.getKey().toString(),
                            entry.getValue(), InterpreterPropertyType.STRING.getValue());
                    newProperties.put(entry.getKey().toString(), newProperty);
                } else {
                    // already converted
                    return (Map<String, InterpreterProperty>) properties;
                }
            }
            return newProperties;

        } else if (properties instanceof Map) {
            Map<String, Object> dProperties = (Map<String, Object>) properties;
            Map<String, InterpreterProperty> newProperties = new HashMap<>();
            for (String key : dProperties.keySet()) {
                Object value = dProperties.get(key);
                if (value instanceof InterpreterProperty) {
                    return (Map<String, InterpreterProperty>) properties;
                } else if (value instanceof StringMap) {
                    StringMap stringMap = (StringMap) value;
                    InterpreterProperty newProperty = new InterpreterProperty(key, stringMap.get("value"),
                            stringMap.containsKey("type") ? stringMap.get("type").toString() : "string");

                    newProperties.put(newProperty.getName(), newProperty);
                } else if (value instanceof DefaultInterpreterProperty) {
                    DefaultInterpreterProperty dProperty = (DefaultInterpreterProperty) value;
                    InterpreterProperty property = new InterpreterProperty(key, dProperty.getValue(),
                            dProperty.getType() != null ? dProperty.getType() : "string"
                    // in case user forget to specify type in interpreter-setting.json
                    );
                    newProperties.put(key, property);
                } else if (value instanceof String) {
                    InterpreterProperty newProperty = new InterpreterProperty(key, value, "string");

                    newProperties.put(newProperty.getName(), newProperty);
                } else {
                    throw new RuntimeException("Can not convert this type of property: " + value.getClass());
                }
            }
            return newProperties;
        }
        throw new RuntimeException("Can not convert this type: " + properties.getClass());
    }

    public void waitForReady() throws InterruptedException {
        while (getStatus()
                .equals(org.apache.zeppelin.interpreter.InterpreterSetting.Status.DOWNLOADING_DEPENDENCIES)) {
            Thread.sleep(200);
        }
    }
}