Skip to content

#317 Restore Paths from file which speed up opening the project second time by 25 times #319

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 132 additions & 27 deletions src/main/java/org/javacs/InferConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.net.URI;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.logging.Logger;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.net.URI;
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;
import java.util.stream.Collectors;

class InferConfig {
private static final Logger LOG = Logger.getLogger("main");
Expand Down Expand Up @@ -49,11 +63,23 @@ private static Path defaultGradleHome() {
return Paths.get(System.getProperty("user.home")).resolve(".gradle");
}


static boolean firstVisitClassPath = true;
static final String classPathSerName = ".jls/classes.ser";

/** Find .jar files for external dependencies, for examples maven dependencies in ~/.m2 or jars in bazel-genfiles */
Set<Path> classPath() {
// externalDependencies
Set<Path> restored = restorePaths(classPathSerName);
LOG.info("restored object " + restored);
if (firstVisitClassPath && restored != null) {
firstVisitClassPath = false;
return restored;
}

Set<Path> result = Collections.emptySet();
if (!externalDependencies.isEmpty()) {
var result = new HashSet<Path>();
result = new HashSet<Path>();
for (var id : externalDependencies) {
var a = Artifact.parse(id);
var found = findAnyJar(a, false);
Expand All @@ -63,22 +89,37 @@ Set<Path> classPath() {
}
result.add(found);
}
return result;
//return result;
} else {

// Maven
var pomXml = workspaceRoot.resolve("pom.xml");
if (Files.exists(pomXml)) {
Set<Path> deps = mvnDependencies(pomXml, "dependency:list");
result = deps;
} else {

// Bazel
var bazelWorkspaceRoot = bazelWorkspaceRoot();
if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) {
result = bazelClasspath(bazelWorkspaceRoot);
}
}
}

// Maven
var pomXml = workspaceRoot.resolve("pom.xml");
if (Files.exists(pomXml)) {
return mvnDependencies(pomXml, "dependency:list");
var classpathEnvVar = System.getenv("CLASSPATH");
LOG.fine(() -> "CLASSPATH=" + classpathEnvVar);
if (classpathEnvVar != null) {
var paths = Stream.of(classpathEnvVar.split(System.getProperty("path.separator"))).map(Path::of).collect(Collectors.toSet());
LOG.fine(() -> "Paths from CLASSPATH = " + paths);
result = paths;
}

// Bazel
var bazelWorkspaceRoot = bazelWorkspaceRoot();
if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) {
return bazelClasspath(bazelWorkspaceRoot);
if (restored == null) {
savePaths(result, classPathSerName);
}

return Collections.emptySet();
LOG.info("class path saved");
return result;
}

private Path bazelWorkspaceRoot() {
Expand All @@ -90,11 +131,23 @@ private Path bazelWorkspaceRoot() {
return workspaceRoot;
}

static boolean firstVisitDocPath = true;
static final String docPathSerName = ".jls/doc.ser";

/** Find source .jar files in local maven repository. */
Set<Path> buildDocPath() {
// externalDependencies
Set<Path> restored = restorePaths(docPathSerName);
LOG.info("restored object " + restored);
if (firstVisitDocPath && restored != null) {
firstVisitDocPath = false;
LOG.info("doc path restored");
return restored;
}

Set<Path> result = Collections.emptySet();
if (!externalDependencies.isEmpty()) {
var result = new HashSet<Path>();
result = new HashSet<Path>();
for (var id : externalDependencies) {
var a = Artifact.parse(id);
var found = findAnyJar(a, true);
Expand All @@ -104,22 +157,24 @@ Set<Path> buildDocPath() {
}
result.add(found);
}
return result;
}
} else {

// Maven
var pomXml = workspaceRoot.resolve("pom.xml");
if (Files.exists(pomXml)) {
return mvnDependencies(pomXml, "dependency:sources");
}
// Maven
var pomXml = workspaceRoot.resolve("pom.xml");
if (Files.exists(pomXml)) {
result = mvnDependencies(pomXml, "dependency:sources");
}

// Bazel
var bazelWorkspaceRoot = bazelWorkspaceRoot();
if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) {
return bazelSourcepath(bazelWorkspaceRoot);
// Bazel
var bazelWorkspaceRoot = bazelWorkspaceRoot();
if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) {
result = bazelSourcepath(bazelWorkspaceRoot);
}
}

return Collections.emptySet();
if (restored == null) {
savePaths(result, docPathSerName);
}
return result;
}

private Path findAnyJar(Artifact artifact, boolean source) {
Expand Down Expand Up @@ -173,9 +228,20 @@ private String fileName(Artifact artifact, boolean source) {
return artifact.artifactId + '-' + artifact.version + (source ? "-sources" : "") + ".jar";
}

static boolean firstVisitMvn = true;
static final String mvnSerName = ".jls/deps.ser";

static Set<Path> mvnDependencies(Path pomXml, String goal) {
Objects.requireNonNull(pomXml, "pom.xml path is null");
try {
Set<Path> restored = restorePaths(mvnSerName);
LOG.info("restored object " + restored);
if (firstVisitMvn && restored != null) {
firstVisitMvn = false;
LOG.info("mvn path restored");
return restored;
}
var dependencies = new HashSet<Path>();
// TODO consider using mvn valide dependency:copy-dependencies -DoutputDirectory=??? instead
// Run maven as a subprocess
String[] command = {
Expand Down Expand Up @@ -203,13 +269,15 @@ static Set<Path> mvnDependencies(Path pomXml, String goal) {
return Set.of();
}
// Read output
var dependencies = new HashSet<Path>();
for (var line : Files.readAllLines(output)) {
var jar = readDependency(line);
if (jar != NOT_FOUND) {
dependencies.add(jar);
}
}
if (restored == null) {
savePaths(dependencies, mvnSerName);
}
return dependencies;
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -497,5 +565,42 @@ private static Path fork(Path workspaceRoot, String[] command) {
}
}

static void saveObject(Object obj, String name) {
try (FileOutputStream fos = new FileOutputStream(name);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(obj);
} catch (Exception e) {
// ignore
LOG.severe(e.getMessage());
}
}

static public Object restoreObject(String name) {
try (FileInputStream fis = new FileInputStream(name);
ObjectInputStream ois = new ObjectInputStream(fis)) {
return ois.readObject();
} catch (Exception e) {
LOG.severe(e.getMessage());
return null;
}
}

static void savePaths(Collection<Path> paths, String name) {
saveObject(paths.stream().map(p -> p.toUri()).toList(), name);
}

static Set<Path> restorePaths(String name) {
Object obj = restoreObject(name);
if (obj != null) {
Set<Path> result = new HashSet<>();
for (URI uri : (Collection<URI>)obj) {
result.add(Paths.get(uri));
}
return result;
}
return null;
}


private static final Path NOT_FOUND = Paths.get("");
}
7 changes: 7 additions & 0 deletions src/main/java/org/javacs/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import org.javacs.lsp.*;
import java.io.File;

public class Main {
private static final Logger LOG = Logger.getLogger("main");
Expand All @@ -19,6 +20,12 @@ public static void setRootFormat() {
public static void main(String[] args) {
boolean quiet = Arrays.stream(args).anyMatch("--quiet"::equals);

// define hidden workspace directory
File directory = new File(".jls");
if (!directory.exists()) {
directory.mkdirs();
}

if (quiet) {
LOG.setLevel(Level.OFF);
}
Expand Down