/* * 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.notebook; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.Input; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterException; import org.apache.zeppelin.interpreter.InterpreterGroup; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.notebook.repo.NotebookRepo; import org.apache.zeppelin.notebook.utility.IdHashes; import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.scheduler.Job.Status; import org.apache.zeppelin.scheduler.JobListener; import org.apache.zeppelin.search.SearchService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; /** * Binded interpreters for a note */ public class Note implements Serializable, JobListener { private static final long serialVersionUID = 7920699076577612429L; final List<Paragraph> paragraphs = new LinkedList<>(); private String name = ""; private String id; @SuppressWarnings("rawtypes") Map<String, List<AngularObject>> angularObjects = new HashMap<>(); private transient NoteInterpreterLoader replLoader; private transient JobListenerFactory jobListenerFactory; private transient NotebookRepo repo; private transient SearchService index; /** * note configurations. * * - looknfeel - cron */ private Map<String, Object> config = new HashMap<>(); /** * note information. * * - cron : cron expression validity. */ private Map<String, Object> info = new HashMap<>(); public Note() {} public Note(NotebookRepo repo, NoteInterpreterLoader replLoader, JobListenerFactory jlFactory, SearchService noteIndex) { this.repo = repo; this.replLoader = replLoader; this.jobListenerFactory = jlFactory; this.index = noteIndex; generateId(); } private void generateId() { id = IdHashes.encode(System.currentTimeMillis() + new Random().nextInt()); } public String id() { return id; } public String getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public NoteInterpreterLoader getNoteReplLoader() { return replLoader; } public void setReplLoader(NoteInterpreterLoader replLoader) { this.replLoader = replLoader; } public JobListenerFactory getJobListenerFactory() { return jobListenerFactory; } public void setJobListenerFactory(JobListenerFactory jobListenerFactory) { this.jobListenerFactory = jobListenerFactory; } public NotebookRepo getNotebookRepo() { return repo; } public void setNotebookRepo(NotebookRepo repo) { this.repo = repo; } public void setIndex(SearchService index) { this.index = index; } @SuppressWarnings("rawtypes") public Map<String, List<AngularObject>> getAngularObjects() { return angularObjects; } /** * Add paragraph last. * * @param p */ public Paragraph addParagraph() { Paragraph p = new Paragraph(this, this, replLoader); synchronized (paragraphs) { paragraphs.add(p); } return p; } /** * Clone paragraph and add it to note. * * @param srcParagraph */ public void addCloneParagraph(Paragraph srcParagraph) { Paragraph newParagraph = new Paragraph(this, this, replLoader); Map<String, Object> config = new HashMap<>(srcParagraph.getConfig()); Map<String, Object> param = new HashMap<>(srcParagraph.settings.getParams()); Map<String, Input> form = new HashMap<>(srcParagraph.settings.getForms()); Gson gson = new Gson(); InterpreterResult result = gson.fromJson( gson.toJson(srcParagraph.getReturn()), InterpreterResult.class); newParagraph.setConfig(config); newParagraph.settings.setParams(param); newParagraph.settings.setForms(form); newParagraph.setText(srcParagraph.getText()); newParagraph.setTitle(srcParagraph.getTitle()); newParagraph.setReturn(result, null); synchronized (paragraphs) { paragraphs.add(newParagraph); } } /** * Insert paragraph in given index. * * @param index * @param p */ public Paragraph insertParagraph(int index) { Paragraph p = new Paragraph(this, this, replLoader); synchronized (paragraphs) { paragraphs.add(index, p); } return p; } /** * Remove paragraph by id. * * @param paragraphId * @return a paragraph that was deleted, or <code>null</code> otherwise */ public Paragraph removeParagraph(String paragraphId) { synchronized (paragraphs) { Iterator<Paragraph> i = paragraphs.iterator(); while (i.hasNext()) { Paragraph p = i.next(); if (p.getId().equals(paragraphId)) { index.deleteIndexDoc(this, p); i.remove(); return p; } } } return null; } /** * Clear paragraph output by id. * * @param paragraphId * @return */ public Paragraph clearParagraphOutput(String paragraphId) { synchronized (paragraphs) { for (int i = 0; i < paragraphs.size(); i++) { Paragraph p = paragraphs.get(i); if (p.getId().equals(paragraphId)) { p.setReturn(null, null); return p; } } } return null; } /** * Move paragraph into the new index (order from 0 ~ n-1). * * @param paragraphId * @param index new index */ public void moveParagraph(String paragraphId, int index) { moveParagraph(paragraphId, index, false); } /** * Move paragraph into the new index (order from 0 ~ n-1). * * @param paragraphId * @param index new index * @param throwWhenIndexIsOutOfBound whether throw IndexOutOfBoundException * when index is out of bound */ public void moveParagraph(String paragraphId, int index, boolean throwWhenIndexIsOutOfBound) { synchronized (paragraphs) { int oldIndex; Paragraph p = null; if (index < 0 || index >= paragraphs.size()) { if (throwWhenIndexIsOutOfBound) { throw new IndexOutOfBoundsException("paragraph size is " + paragraphs.size() + " , index is " + index); } else { return; } } for (int i = 0; i < paragraphs.size(); i++) { if (paragraphs.get(i).getId().equals(paragraphId)) { oldIndex = i; if (oldIndex == index) { return; } p = paragraphs.remove(i); } } if (p != null) { paragraphs.add(index, p); } } } public boolean isLastParagraph(String paragraphId) { if (!paragraphs.isEmpty()) { synchronized (paragraphs) { if (paragraphId.equals(paragraphs.get(paragraphs.size() - 1).getId())) { return true; } } return false; } /** because empty list, cannot remove nothing right? */ return true; } public Paragraph getParagraph(String paragraphId) { synchronized (paragraphs) { for (Paragraph p : paragraphs) { if (p.getId().equals(paragraphId)) { return p; } } } return null; } public Paragraph getLastParagraph() { synchronized (paragraphs) { return paragraphs.get(paragraphs.size() - 1); } } public List<Map<String, String>> generateParagraphsInfo (){ List<Map<String, String>> paragraphsInfo = new LinkedList<>(); synchronized (paragraphs) { for (Paragraph p : paragraphs) { Map<String, String> info = new HashMap<>(); info.put("id", p.getId()); info.put("status", p.getStatus().toString()); if (p.getDateStarted() != null) { info.put("started", p.getDateStarted().toString()); } if (p.getDateFinished() != null) { info.put("finished", p.getDateFinished().toString()); } if (p.getStatus().isRunning()) { info.put("progress", String.valueOf(p.progress())); } paragraphsInfo.add(info); } } return paragraphsInfo; } /** * Run all paragraphs sequentially. * * @param jobListener */ public void runAll() { synchronized (paragraphs) { for (Paragraph p : paragraphs) { p.setNoteReplLoader(replLoader); p.setListener(jobListenerFactory.getParagraphJobListener(this)); Interpreter intp = replLoader.get(p.getRequiredReplName()); intp.getScheduler().submit(p); } } } /** * Run a single paragraph. * * @param paragraphId */ public void run(String paragraphId) { Paragraph p = getParagraph(paragraphId); p.setNoteReplLoader(replLoader); p.setListener(jobListenerFactory.getParagraphJobListener(this)); Interpreter intp = replLoader.get(p.getRequiredReplName()); if (intp == null) { throw new InterpreterException("Interpreter " + p.getRequiredReplName() + " not found"); } if (p.getConfig().get("enabled") == null || (Boolean) p.getConfig().get("enabled")) { intp.getScheduler().submit(p); } } public List<String> completion(String paragraphId, String buffer, int cursor) { Paragraph p = getParagraph(paragraphId); p.setNoteReplLoader(replLoader); p.setListener(jobListenerFactory.getParagraphJobListener(this)); return p.completion(buffer, cursor); } public List<Paragraph> getParagraphs() { synchronized (paragraphs) { return new LinkedList<Paragraph>(paragraphs); } } private void snapshotAngularObjectRegistry() { angularObjects = new HashMap<>(); List<InterpreterSetting> settings = replLoader.getInterpreterSettings(); if (settings == null || settings.size() == 0) { return; } for (InterpreterSetting setting : settings) { InterpreterGroup intpGroup = setting.getInterpreterGroup(); AngularObjectRegistry registry = intpGroup.getAngularObjectRegistry(); angularObjects.put(intpGroup.getId(), registry.getAllWithGlobal(id)); } } public void persist() throws IOException { snapshotAngularObjectRegistry(); index.updateIndexDoc(this); repo.save(this); } public void unpersist() throws IOException { repo.remove(id()); } public Map<String, Object> getConfig() { if (config == null) { config = new HashMap<>(); } return config; } public void setConfig(Map<String, Object> config) { this.config = config; } public Map<String, Object> getInfo() { if (info == null) { info = new HashMap<>(); } return info; } public void setInfo(Map<String, Object> info) { this.info = info; } @Override public void beforeStatusChange(Job job, Status before, Status after) { } @Override public void afterStatusChange(Job job, Status before, Status after) { } @Override public void onProgressUpdate(Job job, int progress) {} }