-
Notifications
You must be signed in to change notification settings - Fork 2.8k
ZEPPELIN-598 ] Dynamic loading for Interpreter and API #908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
3d0a9d4
599639f
1b598b7
3965f74
1bb3a0c
377393a
43ad199
04d84d3
b10b64d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /* | ||
| * 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.rest.message; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * LoadDynamicInterpreterRequest rest api request message | ||
| */ | ||
|
|
||
| public class LoadDynamicInterpreterRequest { | ||
| String artifact; | ||
| String className; | ||
| Map<String, Object> repository; | ||
|
|
||
| public LoadDynamicInterpreterRequest() { | ||
|
|
||
| } | ||
|
|
||
| public String getArtifact() { return artifact; } | ||
|
|
||
| public String getClassName() { return className; } | ||
|
|
||
| public Map<String, Object> getRepository() { return repository; } | ||
|
|
||
| public String getUrl() throws ClassCastException { | ||
| Object urlObj = repository.get("url"); | ||
| if (urlObj == null) { | ||
| return null; | ||
| } | ||
| return (String) urlObj; | ||
| } | ||
|
|
||
| public Boolean isSnapshot() throws ClassCastException { | ||
| Object snapshotFlagObj = repository.get("snapshot"); | ||
| if (snapshotFlagObj == null) { | ||
| return false; | ||
| } | ||
| return (Boolean) snapshotFlagObj; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -503,6 +503,7 @@ public static enum ConfVars { | |
| // Decide when new note is created, interpreter settings will be binded automatically or not. | ||
| ZEPPELIN_NOTEBOOK_AUTO_INTERPRETER_BINDING("zeppelin.notebook.autoInterpreterBinding", true), | ||
| ZEPPELIN_CONF_DIR("zeppelin.conf.dir", "conf"), | ||
| ZEPPELIN_INTERPRETER_DOWNLOAD_DIR("zeppelin.interpreter.download.dir", "interpreter"), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this line might better be placed next to Could you also add new configuration to docs/install/install.md and conf/zeppelin-site.xml.template
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Leemoonsoo |
||
| ZEPPELIN_DEP_LOCALREPO("zeppelin.dep.localrepo", "local-repo"), | ||
| // Allows a way to specify a ',' separated list of allowed origins for rest and websockets | ||
| // i.e. http://localhost:8080 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,7 +60,7 @@ public class InterpreterFactory implements InterpreterGroupFactory { | |
| .synchronizedMap(new HashMap<String, URLClassLoader>()); | ||
|
|
||
| private ZeppelinConfiguration conf; | ||
| String[] interpreterClassList; | ||
| List<String> interpreterClassList; | ||
|
|
||
| private Map<String, InterpreterSetting> interpreterSettings = | ||
| new HashMap<String, InterpreterSetting>(); | ||
|
|
@@ -99,7 +99,10 @@ public InterpreterFactory(ZeppelinConfiguration conf, InterpreterOption defaultO | |
| this.interpreterRepositories = depResolver.getRepos(); | ||
| this.remoteInterpreterProcessListener = remoteInterpreterProcessListener; | ||
| String replsConf = conf.getString(ConfVars.ZEPPELIN_INTERPRETERS); | ||
| interpreterClassList = replsConf.split(","); | ||
| interpreterClassList = new ArrayList<String>(); | ||
| for (String className : replsConf.split(",")) { | ||
| interpreterClassList.add(className); | ||
| } | ||
|
|
||
| GsonBuilder builder = new GsonBuilder(); | ||
| builder.setPrettyPrinting(); | ||
|
|
@@ -110,6 +113,84 @@ public InterpreterFactory(ZeppelinConfiguration conf, InterpreterOption defaultO | |
| init(); | ||
| } | ||
|
|
||
| public boolean loadDynamicInterpreter(String intpGroupName, String intpName, String artifact, | ||
| String intpClassName) { | ||
| return loadDynamicInterpreter(intpGroupName, intpName, artifact, intpClassName, null, false); | ||
| } | ||
|
|
||
| public boolean loadDynamicInterpreter(String intpGroupName, String intpName, String artifact, | ||
| String intpClassName, String repositoryUrl, boolean isSnapShotRepo) { | ||
| String[] artifactItem = artifact.split(":"); | ||
| String zepInterpreterRepoDir = conf.getString(ConfVars.ZEPPELIN_INTERPRETER_DOWNLOAD_DIR); | ||
| String zepInterpreterRepoFullPath = conf.getRelativeDir( | ||
| ConfVars.ZEPPELIN_INTERPRETER_DOWNLOAD_DIR | ||
| ); | ||
| String interpreterDesPath = String.format("%s/%s/%s/", zepInterpreterRepoDir, | ||
| intpGroupName, intpName); | ||
| String interpreterLoadPath = String.format("%s/%s/%s", zepInterpreterRepoFullPath, | ||
| intpGroupName, intpName); | ||
|
|
||
| if (artifactItem.length <= 0) { | ||
| logger.error("Failed load dynamic interpreter - invalid artifact : {}", artifact); | ||
| return false; | ||
| } | ||
|
|
||
| try { | ||
| if (repositoryUrl != null) { | ||
| depResolver.addRepo("dyInterpreterRepo", repositoryUrl, isSnapShotRepo); | ||
| } | ||
| logger.info("interpreter path : {}", interpreterLoadPath); | ||
| depResolver.load(artifact, interpreterDesPath); | ||
| setDynamicInterpreter(intpClassName, interpreterLoadPath); | ||
| } catch (Exception e) { | ||
| logger.error("Failed load dynamic interpreter : ", e); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| protected void setDynamicInterpreter(String interpreterClassName, String fileDirPath) | ||
| throws InterpreterException, IOException { | ||
| logger.info("load Dynamic Interpreter ClassName : {}", interpreterClassName); | ||
| logger.info("load Dynamic Interpreter FilePath : {}", interpreterClassName); | ||
|
|
||
| ClassLoader oldcl = Thread.currentThread().getContextClassLoader(); | ||
| interpreterClassList.add(interpreterClassName); | ||
| // Load classes | ||
| File interpreterDir = new File(fileDirPath); | ||
|
|
||
| if (interpreterDir != null) { | ||
| URL[] urls = null; | ||
| try { | ||
| urls = recursiveBuildLibList(interpreterDir); | ||
| } catch (MalformedURLException e1) { | ||
| logger.error("Can't load jars ", e1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should it abort when it fails? or is this more a warning?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @felixcheung |
||
| } | ||
| URLClassLoader ccl = new URLClassLoader(urls, oldcl); | ||
|
|
||
| try { | ||
| Class.forName(interpreterClassName, true, ccl); | ||
| Set<String> keys = Interpreter.registeredInterpreters.keySet(); | ||
| for (String intName : keys) { | ||
| if (interpreterClassName.equals( | ||
| Interpreter.registeredInterpreters.get(intName).getClassName())) { | ||
| Interpreter.registeredInterpreters.get(intName).setPath(fileDirPath); | ||
| logger.info("Interpreter {} found. class={}", intName, fileDirPath); | ||
| cleanCl.put(fileDirPath, ccl); | ||
| } | ||
| } | ||
| } catch (ClassNotFoundException e) { | ||
| logger.error("Load error : ", e); | ||
| } | ||
| } | ||
|
|
||
| for (String settingId : interpreterSettings.keySet()) { | ||
| InterpreterSetting setting = interpreterSettings.get(settingId); | ||
| logger.info("Interpreter setting group {} : id={}, name={}", | ||
| setting.getGroup(), settingId, setting.getName()); | ||
| } | ||
| } | ||
|
|
||
| private void init() throws InterpreterException, IOException, RepositoryException { | ||
| ClassLoader oldcl = Thread.currentThread().getContextClassLoader(); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something like "Path for interpreter download" is more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Leemoonsoo It is meant for Comments?