Skip to content

Commit

Permalink
implement local storage
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrilou242 committed Jul 8, 2023
1 parent 848aa3f commit 7e2fd2b
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 9 deletions.
24 changes: 17 additions & 7 deletions jnotebook-core/src/main/java/tech/catheu/jnotebook/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@
import picocli.CommandLine;

import java.io.IOException;
import java.nio.file.Paths;

@CommandLine.Command(name = "jnotebook", subcommands = {Main.InteractiveServerCommand.class, Main.RenderCommand.class})
public class Main {

private static final Logger LOG = LoggerFactory.getLogger(Main.class);
public static final String USER_HOME = System.getProperty("user.home");

public static void main(String[] args) {
final CommandLine commandLine = new CommandLine(new InteractiveCommand());
commandLine.addSubcommand(new RenderCommand());
int exitCode = commandLine.execute(args);
int exitCode = new CommandLine(new Main()).execute(args);
System.exit(exitCode);
}

@CommandLine.Command(name = "start", mixinStandardHelpOptions = true,
@CommandLine.Command(name = "server", mixinStandardHelpOptions = true,
description = "Start the interactive notebook server.")
public static class InteractiveCommand implements Runnable {
public static class InteractiveServerCommand implements Runnable {
@CommandLine.Mixin
private InteractiveConfiguration config;

Expand All @@ -56,10 +57,19 @@ public void run() {
public static class SharedConfiguration {
// notebook runtime configs
@CommandLine.Option(names = {"-cp", "-classpath", "--class-path"},
paramLabel = "class search path of directories and zip/jar files",
paramLabel = "<CLASSPATH>",
description = "A : separated list of directories, JAR archives,\n and ZIP archives to search for class files.",
defaultValue = "")
public String classPath = "";

@CommandLine.Option(names = {"--local-storage-path"},
paramLabel = "<PATH>",
description = "The fullpath to a folder to use as jnotebook local storage, where extensions and states are cached.")
public String localStoragePath = Paths.get(USER_HOME, ".jnotebook").toString();

@CommandLine.Option(names = {"--no-utils"},
description = "Flag to disable the injection of jnotebook-utils jar.")
public boolean noUtils = false;
}


Expand All @@ -69,7 +79,7 @@ public static class InteractiveConfiguration extends SharedConfiguration {
public String notebookPath = "notebooks";

@CommandLine.Option(names = {"-p", "--port"},
paramLabel = "server port",
paramLabel = "<PORT>",
description = "Port of the notebook server",
defaultValue = "5002")
public Integer port;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.catheu.jnotebook.localstorage.LocalStorage;

import java.io.BufferedReader;
import java.io.File;
Expand All @@ -39,10 +40,12 @@ public class ShellProvider {
private final Main.SharedConfiguration configuration;

private String resolvedClasspath = null;
private final LocalStorage localStorage;

public ShellProvider(Main.SharedConfiguration configuration) {
public ShellProvider(final Main.SharedConfiguration configuration) {
this.configuration = configuration;
this.preparedShells = new ArrayDeque<>(2);
this.localStorage = LocalStorage.instanceFor(configuration);

warmUp();
}
Expand Down Expand Up @@ -89,6 +92,13 @@ private String getClassPath() {
resolvedClasspath = configuration.classPath;
}

if (configuration.noUtils) {
LOG.info("Skipping injection of notebook utils in the classpath.");
} else {
LOG.info("Injecting notebook utils in the classpath.");
resolvedClasspath += ":" + localStorage.getUtilsPath();
}

return resolvedClasspath;
}

Expand Down Expand Up @@ -123,7 +133,8 @@ private String computeMavenClasspath() throws IOException, InterruptedException
/**
* Look for a file. If not found, look into the parent.
*/
private static File lookForFile(final String filename, final File startDirectory, final int depthLimit) {
private static File lookForFile(final String filename, final File startDirectory,
final int depthLimit) {
final File absoluteDirectory = startDirectory.getAbsoluteFile();
if (new File(absoluteDirectory, filename).exists()) {
return new File(absoluteDirectory, filename);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright Cyril de Catheu, 2023
*
* Licensed under the JNOTEBOOK LICENSE 1.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at https://raw.githubusercontent.com/cyrilou242/jnotebook/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OF ANY KIND,
* either express or implied.
* See the License for the specific language governing permissions and limitations under
* the License.
*/
package tech.catheu.jnotebook.localstorage;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.catheu.jnotebook.Main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class LocalStorage {

private static final Logger LOG = LoggerFactory.getLogger(LocalStorage.class);
private static final String RESOURCES_UTILS_FOLDER = "/utils/";
private static final String LOCAL_STORAGE_UTILS_FOLDER = "utils";

private static LocalStorage instance;
private final Main.SharedConfiguration config;

private LocalStorage(final Main.SharedConfiguration config) {
this.config = config;
}

public static LocalStorage instanceFor(final Main.SharedConfiguration configuration) {
if (instance == null) {
instance = new LocalStorage(configuration);
}
checkArgument(configuration.localStoragePath.equals(instance.config.localStoragePath));
return instance;
}

/**
* Returns the path of the jnotebook-utils jar.
* If it does not exist in the local storage, attempts to create it.
*/
public String getUtilsPath() {
final String resourceUtilsJarName = getUtilsJarName();
final Path localStorageUtilsJarPath = Paths.get(config.localStoragePath,
LOCAL_STORAGE_UTILS_FOLDER,
resourceUtilsJarName);
final boolean isSnapshot =
resourceUtilsJarName.toLowerCase(Locale.ENGLISH).contains("snapshot");
final boolean isInLocalStorage = Files.exists(localStorageUtilsJarPath);
if (isSnapshot || !isInLocalStorage) {
LOG.info("Copying {} to local storage", resourceUtilsJarName);
final Path resourcesUtilsJarPath =
Paths.get(RESOURCES_UTILS_FOLDER, resourceUtilsJarName);
copyResourcesToFile(resourcesUtilsJarPath, localStorageUtilsJarPath);
}
return localStorageUtilsJarPath.toString();
}

private static void copyResourcesToFile(final Path resourcesPath, final Path filePath) {
try (final InputStream in = LocalStorage.class.getResourceAsStream(resourcesPath.toString())) {
checkState(in != null, "Failed reading %s from resources", resourcesPath);
createIfNotExists(filePath.getParent());
Files.copy(in, filePath, REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(String.format("Failed copying resource %s to file %s: %s",
resourcesPath,
filePath,
e));
}
}

private String getUtilsJarName() {
// the resources utils folder should contain a single file jnotebook-utils-VERSION.jar
final List<String> files = listFiles(RESOURCES_UTILS_FOLDER);
checkState(files.size() == 1);
return files.get(0);
}

private static void createIfNotExists(final Path folderPath) {
try {
if (!Files.exists(folderPath)) {
LOG.info("{} folder does not exist. Creating folder.", folderPath);
Files.createDirectories(folderPath);
}
} catch (Exception e) {
LOG.error("Failed creating folder {}. Error: {}", folderPath, e.getMessage());
throw new RuntimeException(e); // don't try to recover if the system is not able to create folders
}
}

@NonNull
private static List<String> listFiles(final String resourcePath) {
final List<String> files = new ArrayList<>();
try (final InputStream in = LocalStorage.class.getResourceAsStream(resourcePath); final BufferedReader br = new BufferedReader(
new InputStreamReader(in))) {
String resource;
while ((resource = br.readLine()) != null) {
files.add(resource);
}
} catch (IOException e) {
throw new RuntimeException(String.format("Could not load alert json: %s", e));
}
return files;
}

}

0 comments on commit 7e2fd2b

Please sign in to comment.