diff --git a/src/main/java/meghanada/junit/TestRunner.java b/src/main/java/meghanada/junit/TestRunner.java index 393397cf3..b3640ae8f 100644 --- a/src/main/java/meghanada/junit/TestRunner.java +++ b/src/main/java/meghanada/junit/TestRunner.java @@ -12,7 +12,6 @@ import meghanada.reflect.ClassIndex; import meghanada.reflect.asm.CachedASMReflector; import meghanada.store.ProjectDatabaseHelper; -import meghanada.utils.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.runner.JUnitCore; @@ -59,7 +58,7 @@ public static void main(String... args) throws Exception { private void cleanup() throws Exception { ProjectDatabaseHelper.shutdown(); String p = System.getProperty(TEMP_PROJECT_SETTING_DIR); - FileUtils.deleteFiles(new File(p), true); + org.apache.commons.io.FileUtils.deleteDirectory(new File(p)); } private List> getTestClass(String testName) throws ClassNotFoundException { diff --git a/src/main/java/meghanada/location/LocationSearcher.java b/src/main/java/meghanada/location/LocationSearcher.java index a102c4e95..75d3518d3 100644 --- a/src/main/java/meghanada/location/LocationSearcher.java +++ b/src/main/java/meghanada/location/LocationSearcher.java @@ -638,7 +638,7 @@ private Location searchLocationFromDecompileFile( } return null; } finally { - FileUtils.deleteFiles(output, false); + org.apache.commons.io.FileUtils.deleteDirectory(output); } } diff --git a/src/main/java/meghanada/project/Project.java b/src/main/java/meghanada/project/Project.java index e4e1b9f7b..fe30f1ac5 100644 --- a/src/main/java/meghanada/project/Project.java +++ b/src/main/java/meghanada/project/Project.java @@ -840,7 +840,7 @@ public void clearCache() throws IOException { System.setProperty(PROJECT_ROOT_KEY, this.projectRootPath); final File projectSettingDir = new File(Config.load().getProjectSettingDir()); log.info("clear cache {}", projectSettingDir); - FileUtils.deleteFiles(projectSettingDir, false); + org.apache.commons.io.FileUtils.deleteDirectory(projectSettingDir); } private Optional readFormatPropertiesFromFile() { diff --git a/src/main/java/meghanada/reflect/ClassIndex.java b/src/main/java/meghanada/reflect/ClassIndex.java index 89f03f752..afdfc0977 100644 --- a/src/main/java/meghanada/reflect/ClassIndex.java +++ b/src/main/java/meghanada/reflect/ClassIndex.java @@ -19,6 +19,7 @@ public class ClassIndex implements CandidateUnit, Cloneable, Serializable, Storable { public static final String ENTITY_TYPE = "ClassIndex"; + public static final String FILE_ENTITY_TYPE = "ClassIndexFile"; private static final long serialVersionUID = 4833311903131990013L; // fqcn @@ -33,6 +34,7 @@ public class ClassIndex implements CandidateUnit, Cloneable, Serializable, Stora private String filePath; private MemberType memberType = MemberType.CLASS; private EntityId entityID; + public transient boolean loaded; public ClassIndex( final String declaration, final List typeParameters, final List supers) { diff --git a/src/main/java/meghanada/reflect/asm/CachedASMReflector.java b/src/main/java/meghanada/reflect/asm/CachedASMReflector.java index 475a76753..d60e3aef7 100644 --- a/src/main/java/meghanada/reflect/asm/CachedASMReflector.java +++ b/src/main/java/meghanada/reflect/asm/CachedASMReflector.java @@ -133,13 +133,29 @@ public void createClassIndexes() { .forEach( wrapIOConsumer( root -> { - // TODO is loaded ? - final ASMReflector reflector = ASMReflector.getInstance(); - reflector - .getClasses(root) - .entrySet() - .parallelStream() - .forEach(entry -> addClassIndex(entry.getKey(), entry.getValue())); + String name = root.getName(); + if (name.endsWith(".jar") + && !name.endsWith("SNAPSHOT.jar") + && ProjectDatabaseHelper.getLoadJar(root.getPath())) { + List indexes = + ProjectDatabaseHelper.getClassIndexes(root.getPath()); + + for (ClassIndex index : indexes) { + index.loaded = true; + String fqcn = index.getRawDeclaration(); + this.globalClassIndex.put(fqcn, index); + } + } else { + final ASMReflector reflector = ASMReflector.getInstance(); + reflector + .getClasses(root) + .entrySet() + .parallelStream() + .forEach(entry -> addClassIndex(entry.getKey(), entry.getValue())); + if (name.endsWith(".jar") && !name.endsWith("SNAPSHOT.jar")) { + ProjectDatabaseHelper.saveLoadJar(root.getPath()); + } + } })); this.updateClassIndexFromDirectory(); @@ -147,18 +163,20 @@ public void createClassIndexes() { } private void saveAllClassIndexes() { - List jarIndexes = - globalClassIndex - .values() - .stream() - .filter(classIndex -> classIndex.getFilePath().endsWith(".jar")) - .collect(Collectors.toList()); - List otherIndexes = - globalClassIndex - .values() - .stream() - .filter(classIndex -> !classIndex.getFilePath().endsWith(".jar")) - .collect(Collectors.toList()); + List jarIndexes = new ArrayList<>(); + List otherIndexes = new ArrayList<>(); + globalClassIndex + .values() + .forEach( + index -> { + if (!index.getFilePath().endsWith(".jar")) { + otherIndexes.add(index); + } else { + if (!index.loaded) { + jarIndexes.add(index); + } + } + }); ProjectDatabaseHelper.saveClassIndexes(jarIndexes, false); ProjectDatabaseHelper.saveClassIndexes(otherIndexes, true); diff --git a/src/main/java/meghanada/store/ProjectDatabase.java b/src/main/java/meghanada/store/ProjectDatabase.java index 28c74333f..ed4c45cfb 100644 --- a/src/main/java/meghanada/store/ProjectDatabase.java +++ b/src/main/java/meghanada/store/ProjectDatabase.java @@ -45,7 +45,6 @@ import meghanada.Main; import meghanada.config.Config; import meghanada.project.Project; -import meghanada.utils.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -89,7 +88,7 @@ private ProjectDatabase() { })); } - public static ProjectDatabase getInstance() { + public static synchronized ProjectDatabase getInstance() { checkChangeProject(); if (projectDatabase != null) { projectDatabase.open(); @@ -330,7 +329,7 @@ private void open() { if (nonNull(files)) { for (File file : files) { if (file.isDirectory() && file.getName().startsWith(name) && !file.equals(base)) { - FileUtils.deleteFiles(file, true); + org.apache.commons.io.FileUtils.deleteDirectory(file); } } } @@ -339,7 +338,7 @@ private void open() { this.environment = Environments.newInstance(base); } catch (ExodusException ex) { // try re-create - FileUtils.deleteFiles(base, true); + org.apache.commons.io.FileUtils.deleteDirectory(base); this.environment = Environments.newInstance(base); } this.entityStore = PersistentEntityStores.newInstance(environment, STORE_NAME); diff --git a/src/main/java/meghanada/store/ProjectDatabaseHelper.java b/src/main/java/meghanada/store/ProjectDatabaseHelper.java index 2519eeb34..a0640e5e2 100644 --- a/src/main/java/meghanada/store/ProjectDatabaseHelper.java +++ b/src/main/java/meghanada/store/ProjectDatabaseHelper.java @@ -1,6 +1,7 @@ package meghanada.store; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import java.io.File; import java.io.IOException; @@ -46,6 +47,45 @@ public static void saveClassIndexes(Collection indexes, boolean allo } } + public static boolean getLoadJar(String filePath) { + ProjectDatabase database = ProjectDatabase.getInstance(); + return database.computeInReadonly( + txn -> { + EntityIterable it = txn.find(ClassIndex.FILE_ENTITY_TYPE, "filePath", filePath); + return nonNull(it.getFirst()); + }); + } + + public static void saveLoadJar(String filePath) { + ProjectDatabase database = ProjectDatabase.getInstance(); + database.execute( + txn -> { + EntityIterable it = txn.find(ClassIndex.FILE_ENTITY_TYPE, "filePath", filePath); + if (nonNull(it.getFirst())) { + return false; + } + Entity entity = txn.newEntity(ClassIndex.FILE_ENTITY_TYPE); + entity.setProperty("filePath", filePath); + return true; + }); + } + + public static List getClassIndexes(String filePath) { + ProjectDatabase database = ProjectDatabase.getInstance(); + return database.find( + ClassIndex.ENTITY_TYPE, + "filePath", + filePath, + entity -> { + try (InputStream in = entity.getBlob(ProjectDatabase.SERIALIZE_KEY)) { + return Serializer.readObject(in, ClassIndex.class); + } catch (Exception e) { + log.warn(e.getMessage()); + return null; + } + }); + } + public static File getClassFile(String fqcn) { ProjectDatabase database = ProjectDatabase.getInstance(); Optional res = diff --git a/src/main/java/meghanada/utils/FileUtils.java b/src/main/java/meghanada/utils/FileUtils.java index af996d581..e78b4a9e5 100644 --- a/src/main/java/meghanada/utils/FileUtils.java +++ b/src/main/java/meghanada/utils/FileUtils.java @@ -121,36 +121,6 @@ public static Optional collectFile(final File root, final String ext) thro } } - public static void deleteFiles(final File root, final boolean deleteRoot) throws IOException { - if (!root.exists()) { - return; - } - Files.walkFileTree( - root.toPath(), - new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) - throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) - throws IOException { - if (deleteRoot) { - Files.delete(dir); - } else { - if (!dir.toFile().equals(root)) { - Files.delete(dir); - } - } - - return FileVisitResult.CONTINUE; - } - }); - } - public static boolean filterFile(final File file) { final Config config = Config.load(); diff --git a/src/test/java/meghanada/GradleTestBase.java b/src/test/java/meghanada/GradleTestBase.java index baf9e55a6..ee8623cdf 100644 --- a/src/test/java/meghanada/GradleTestBase.java +++ b/src/test/java/meghanada/GradleTestBase.java @@ -125,6 +125,7 @@ protected static File getTestOutput() { public static void setupReflector() throws Exception { setupProject(); CachedASMReflector cachedASMReflector = CachedASMReflector.getInstance(); + cachedASMReflector.getGlobalClassIndex().clear(); addClasspath(cachedASMReflector); final Stopwatch stopwatch = Stopwatch.createStarted(); cachedASMReflector.createClassIndexes(); @@ -136,11 +137,11 @@ public static void shutdown() throws IOException, InterruptedException { ProjectDatabaseHelper.shutdown(); String p = System.getProperty(TEMP_PROJECT_SETTING_DIR); File file = new File(p); - FileUtils.deleteFiles(file, true); + org.apache.commons.io.FileUtils.deleteDirectory(file); assert !file.exists(); String tempPath = GradleProject.getTempPath(); if (nonNull(tempPath)) { - FileUtils.deleteFiles(new File(tempPath), true); + org.apache.commons.io.FileUtils.deleteDirectory(new File(tempPath)); } log.info("deleted database {}", file); } diff --git a/src/test/java/meghanada/project/gradle/GradleProjectTest.java b/src/test/java/meghanada/project/gradle/GradleProjectTest.java index 7ec5a6d29..7315e19dd 100644 --- a/src/test/java/meghanada/project/gradle/GradleProjectTest.java +++ b/src/test/java/meghanada/project/gradle/GradleProjectTest.java @@ -14,7 +14,6 @@ import meghanada.project.Project; import meghanada.reflect.asm.CachedASMReflector; import meghanada.store.ProjectDatabaseHelper; -import meghanada.utils.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.AfterClass; @@ -36,10 +35,10 @@ public static void shutdown() throws Exception { ProjectDatabaseHelper.shutdown(); String p = System.getProperty(TEMP_PROJECT_SETTING_DIR); File file = new File(p); - FileUtils.deleteFiles(file, true); + org.apache.commons.io.FileUtils.deleteDirectory(file); String tempPath = GradleProject.getTempPath(); if (nonNull(tempPath)) { - FileUtils.deleteFiles(new File(tempPath), true); + org.apache.commons.io.FileUtils.deleteDirectory(new File(tempPath)); } } diff --git a/src/test/java/meghanada/store/ProjectDatabaseTest.java b/src/test/java/meghanada/store/ProjectDatabaseTest.java index 00d8523e2..e399e9262 100644 --- a/src/test/java/meghanada/store/ProjectDatabaseTest.java +++ b/src/test/java/meghanada/store/ProjectDatabaseTest.java @@ -13,7 +13,6 @@ import java.util.List; import meghanada.project.Project; import meghanada.reflect.ClassIndex; -import meghanada.utils.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -31,8 +30,7 @@ public void setup() throws Exception { tempDir.deleteOnExit(); final String path = tempDir.getCanonicalPath(); System.setProperty(TEMP_PROJECT_SETTING_DIR, path); - FileUtils.deleteFiles(new File(path), true); - + org.apache.commons.io.FileUtils.deleteDirectory(new File(path)); String projectRoot = new File(".").getCanonicalPath(); System.setProperty(Project.PROJECT_ROOT_KEY, projectRoot); @@ -45,7 +43,7 @@ public void tearDown() throws InterruptedException, IOException { database.shutdown(); } String p = System.getProperty(TEMP_PROJECT_SETTING_DIR); - FileUtils.deleteFiles(new File(p), true); + org.apache.commons.io.FileUtils.deleteDirectory(new File(p)); } @Test