PT #157196600: Sys libs jandex index located at ~/.sts4-jandex/

This commit is contained in:
BoykoAlex
2018-05-04 10:23:16 -04:00
parent 15e3230e3b
commit a6d5e3c64f
10 changed files with 223 additions and 47 deletions

View File

@@ -14,13 +14,11 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
@@ -31,10 +29,10 @@ import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.FileObserver;
import org.springframework.ide.vscode.commons.util.Log;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@@ -78,9 +76,9 @@ public final class JandexClasspath implements ClasspathIndex {
attachFolderListeners();
Collection<File> classpathEntries = ImmutableList.of();
try {
classpathEntries = IClasspathUtil.getBinaryRoots(classpath);
classpathEntries = IClasspathUtil.getBinaryRoots(classpath, (cpe) -> !cpe.isSystem());
} catch (Exception e) {
Log.log(e);
log.error("Cannot obtain binary root from classpath entries for " + classpath.getName(), e);
}
return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), classpathResource -> {
switch (providerType) {
@@ -101,12 +99,10 @@ public final class JandexClasspath implements ClasspathIndex {
subscriptions.dispose();
subscriptions = Disposables.composite();
}
for (File cpe : IClasspathUtil.getBinaryRoots(classpath)) {
if (!cpe.toString().endsWith(".jar")) {
final List<String> rebuildGlobPattern = Arrays.asList(cpe.toString().replace(File.separator, "/") + "/**/*.class");
Disposable disposable = fileObserver.onAnyChange(rebuildGlobPattern, (uri) -> reindex());
subscriptions.add(disposable);
}
for (File cpe : IClasspathUtil.getBinaryRoots(classpath, Classpath::isSource)) {
final List<String> rebuildGlobPattern = Arrays.asList(cpe.toString().replace(File.separator, "/") + "/**/*.class");
Disposable disposable = fileObserver.onAnyChange(rebuildGlobPattern, (uri) -> reindex());
subscriptions.add(disposable);
}
}
@@ -131,7 +127,7 @@ public final class JandexClasspath implements ClasspathIndex {
}
protected JandexIndex[] getBaseIndices() {
return new JandexIndex[0];
return JandexSystemLibsIndex.getInstance().fromJars(IClasspathUtil.getBinaryRoots(classpath, CPE::isSystem));
}
@Override

View File

@@ -34,6 +34,8 @@ import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.JarIndexer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.IAnnotation;
import org.springframework.ide.vscode.commons.java.IField;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
@@ -41,7 +43,6 @@ import org.springframework.ide.vscode.commons.java.IMethod;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import org.springframework.ide.vscode.commons.util.Log;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@@ -55,6 +56,8 @@ import reactor.util.function.Tuples;
public class JandexIndex {
private static final Logger log = LoggerFactory.getLogger(JandexIndex.class);
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
@FunctionalInterface
@@ -132,7 +135,7 @@ public class JandexIndex {
knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList())));
});
}
private Optional<IndexView> createIndex(File file, IndexFileFinder indexFileFinder) {
if (file != null && file.isFile() && file.getName().endsWith(".jar")) {
return indexJar(file, indexFileFinder);
@@ -160,7 +163,7 @@ public class JandexIndex {
}
}
} catch (Exception e) {
Log.log(e);
log.error("Failed to index file " + file, e);
}
}
}
@@ -171,27 +174,30 @@ public class JandexIndex {
File indexFile = indexFileFinder.findIndexFile(file);
if (indexFile != null) {
try {
if (!indexFile.getParentFile().exists()) {
indexFile.getParentFile().mkdirs();
}
if (indexFile.createNewFile()) {
try {
return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false,
false, System.out, System.err).getIndex());
} catch (IOException e) {
Log.log("Failed to index '" + file + "'", e);
log.error("Failed to index '" + file + "'", e);
}
} else {
try {
return Optional.of(new IndexReader(new FileInputStream(indexFile)).read());
} catch (IOException e) {
Log.log("Failed to read index file '" + indexFile + "'. Creating new index file.", e);
log.error("Failed to read index file '" + indexFile + "'. Creating new index file.", e);
if (indexFile.delete()) {
return indexJar(file, indexFileFinder);
} else {
Log.log("Failed to read index file '" + indexFile);
log.error("Failed to read index file '" + indexFile);
}
}
}
} catch (IOException e) {
Log.log("Unable to create index file '" + indexFile + "'");
log.error("Unable to create index file '" + indexFile + "'", e);
}
} else {
try {
@@ -199,7 +205,7 @@ public class JandexIndex {
.createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false)
.getIndex());
} catch (IOException e) {
Log.log("Failed to index '" + file + "'", e);
log.error("Failed to index '" + file + "'", e);
}
}
return Optional.empty();
@@ -227,7 +233,7 @@ public class JandexIndex {
.orElse(null));
}
private Optional<Tuple2<File, ClassInfo>> findMatch(DotName fqName) {
return (baseIndex == null ? Stream.<Optional<Tuple2<File, ClassInfo>>>empty()
: Arrays.stream(
@@ -248,7 +254,7 @@ public class JandexIndex {
.filter(t -> t.getT2().isPresent())
.map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst());
}
public Optional<File> findClasspathResourceForType(String fqName) {
Optional<Tuple2<File, ClassInfo>> match = findMatch(DotName.createSimple(fqName));
return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null);
@@ -266,7 +272,7 @@ public class JandexIndex {
return provider == null ? ABSENT_JAVADOC_PROVIDER : provider;
});
} catch (ExecutionException e) {
Log.log(e);
log.error("Failed to retrieve javadoc provider for resource " + classpathResource, e);
}
return Wrappers.wrap(this, match.getT2(), javadocProvider);
}

View File

@@ -0,0 +1,171 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.jandex;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.IllegalSelectorException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.io.BaseEncoding;
/**
* Keeps cache of system libs Jandex Indexes
*
* @author Alex Boyko
*
*/
public class JandexSystemLibsIndex {
private static final String DEFAULT_JAVA_VERSION = "1.8.0";
private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("^java version \"(.*)\"$");
public static final Logger log = LoggerFactory.getLogger(JandexSystemLibsIndex.class);
private static final Supplier<JandexSystemLibsIndex> INSTANCE = Suppliers.memoize(() -> new JandexSystemLibsIndex());
private Cache<Path, JandexIndex> cache;
private JandexSystemLibsIndex() {
this.cache = CacheBuilder.newBuilder().build(new CacheLoader<Path, JandexIndex>() {
@Override
public JandexIndex load(Path key) throws Exception {
return createIndex(key);
}
});
}
/**
* Retrieves or lazily creates Jandex Index for a folder containing system lib jars
* @param path the path containing jars
* @return Jandex Index of the jars contained in the folder
*/
public JandexIndex index(Path path) {
try {
return cache.get(path, () -> createIndex(path));
} catch (ExecutionException e) {
log.error("Failed to detrmine Jandex index for " + path, e);
return null;
}
}
/**
* Retrieves Jandex Indexes appropriate for systm lib jars. One Jandex Index may contain all sys lib jars.
* @param jars system lib jars
* @return Jandex Indexs for jars
*/
public JandexIndex[] fromJars(Collection<File> jars) {
return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(JandexIndex[]::new);
}
public static JandexSystemLibsIndex getInstance() {
return INSTANCE.get();
}
private JandexIndex createIndex(Path path) {
List<File> jars = Collections.emptyList();
try {
jars = Files.list(path).filter(p -> p.getFileName().toString().endsWith(".jar") && Files.isRegularFile(p)).map(p -> p.toFile()).collect(Collectors.toList());
} catch (IOException e) {
// Shouldn't happen - there should at least be one jar file
log.error("Cannot list files in folder " + path, e);
}
return new JandexIndex(jars, jarFile -> findIndexFile(jarFile), (classpathResource) -> createHtmlJavadocProvider(path));
}
private File findIndexFile(File jarFile) {
return Paths.get(System.getProperty("user.home"), ".sts4-jandex", folderNameforPath(jarFile.getParentFile().toString()), jarFile.getName() + ".jdx").toFile();
}
private static String folderNameforPath(String path) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] bytes = md.digest((path).getBytes());
String name = new String(BaseEncoding.base32().encode(bytes));
name = name.replace('/', '_'); //slashes are trouble in file names.
return name;
} catch (NoSuchAlgorithmException e) {
// shouldn't happen!
throw new IllegalSelectorException();
}
}
private static HtmlJavadocProvider createHtmlJavadocProvider(Path path) {
try {
String javaVersion = getJavaVersion(path);
URL javadocUrl = new URL("https://docs.oracle.com/javase/" + extractVersionForJavadoc(javaVersion) + "/docs/api/");
return new HtmlJavadocProvider((type) -> TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER.url(javadocUrl, type.getFullyQualifiedName()));
} catch (MalformedURLException e) {
return null;
}
}
private static String getJavaVersion(Path path) {
// Find valid /bin folder
for (; path != null && !(Files.isDirectory(path.resolve("bin")) && Files.isReadable(path.resolve("bin"))); path = path.getParent());
// If found, assume it's the java home bin folder
if (path != null) {
Path javaBin = path.resolve("bin");
try {
Process p = new ProcessBuilder().directory(javaBin.toFile()).command("./java", "-version").start();
BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getErrorStream()));
int exitCode = p.waitFor();
if (exitCode == 0) {
return buffer.lines().map(l -> JAVA_VERSION_PATTERN.matcher(l)).filter(m -> m.find()).findFirst().map(m -> m.group(1)).orElse(DEFAULT_JAVA_VERSION);
} else {
log.error("Failed to compute java version in folder: " + javaBin + ". 'java -version' exit code is " + exitCode);
}
} catch (IOException | InterruptedException e) {
log.error("Failed to compute java version in folder: " + javaBin, e);
}
}
return DEFAULT_JAVA_VERSION;
}
private static String extractVersionForJavadoc(String javaVersion) {
if (javaVersion.startsWith("1.")) {
int idx = javaVersion.indexOf('.', 2);
return idx >= 0 ? javaVersion.substring(2, idx) : javaVersion.substring(2);
} else {
int idx = javaVersion.indexOf('.');
return idx >= 0 ? javaVersion.substring(0, idx) : javaVersion;
}
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,20 +11,23 @@
package org.springframework.ide.vscode.commons.java;
import java.io.File;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BootProjectUtil {
public static final Logger log = LoggerFactory.getLogger(BootProjectUtil.class);
public static boolean isBootProject(IJavaProject jp) {
try {
IClasspath cp = jp.getClasspath();
if (cp!=null) {
return IClasspathUtil.getBinaryRoots(cp).stream().anyMatch(cpe -> isBootEntry(cpe));
return IClasspathUtil.getBinaryRoots(cp, (cpe) -> !cpe.isSystem()).stream().anyMatch(cpe -> isBootEntry(cpe));
}
} catch (Exception e) {
Log.log(e);
log.error("Failed to determine whether '" + jp.getElementName() + "' is Spring Boot project", e);
}
return false;
}

View File

@@ -12,19 +12,15 @@ package org.springframework.ide.vscode.commons.java;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
import org.springframework.ide.vscode.commons.util.Assert;
import com.google.common.collect.ImmutableList;
@@ -45,14 +41,20 @@ public class IClasspathUtil {
return null;
}
public static List<File> getBinaryRoots(IClasspath cp) {
public static List<File> getAllBinaryRoots(IClasspath cp) {
return getBinaryRoots(cp, null);
}
public static List<File> getBinaryRoots(IClasspath cp, Predicate<CPE> filter) {
ImmutableList.Builder<File> roots = ImmutableList.builder();
try {
for (CPE cpe : cp.getClasspathEntries()) {
File loc = binaryLocation(cpe);
if (loc!=null) {
roots.add(loc);
}
if (filter == null || filter.test(cpe)) {
File loc = binaryLocation(cpe);
if (loc!=null) {
roots.add(loc);
}
}
}
} catch (Exception e) {
log.error("", e);

View File

@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.handlers;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
@@ -268,7 +267,7 @@ public class BootJavaHoverProvider implements HoverHandler {
try {
IClasspath classpath = project.getClasspath();
if (classpath!=null) {
return IClasspathUtil.getBinaryRoots(classpath).stream().anyMatch(cpe -> {
return IClasspathUtil.getBinaryRoots(classpath, (cpe) -> !cpe.isSystem()).stream().anyMatch(cpe -> {
String name = cpe.getName();
return name.startsWith("spring-boot-actuator-");
});

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.boot.java.handlers;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@@ -127,7 +126,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler {
private String[] getClasspathEntries(IDocument doc) throws Exception {
IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get();
IClasspath classpath = project.getClasspath();
Stream<File> classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream();
Stream<File> classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream();
return classpathEntries
.filter(file -> file.exists())
.map(file -> file.getAbsolutePath())

View File

@@ -173,7 +173,7 @@ public final class CompilationUnitCache {
return new String[0];
} else {
IClasspath classpath = project.getClasspath();
Stream<File> classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream();
Stream<File> classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream();
return classpathEntries
.filter(file -> file.exists())
.map(file -> file.getAbsolutePath()).toArray(String[]::new);

View File

@@ -527,7 +527,7 @@ public class SpringIndexer {
private String[] getClasspathEntries(IJavaProject project) throws Exception {
IClasspath classpath = project.getClasspath();
Stream<File> classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream();
Stream<File> classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream();
return classpathEntries
.filter(file -> file.exists())
.map(file -> file.getAbsolutePath())

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -56,7 +56,7 @@ public class PropertiesLoader {
public ConfigurationMetadataRepository load(IClasspath classPath) {
try {
IClasspathUtil.getBinaryRoots(classPath).forEach(fileEntry -> {
IClasspathUtil.getBinaryRoots(classPath, (cpe) -> !cpe.isSystem()).forEach(fileEntry -> {
if (fileEntry.exists()) {
if (fileEntry.isDirectory()) {
loadFromOutputFolder(fileEntry.toPath());