Skip to content

Commit

Permalink
3.2.12.1
Browse files Browse the repository at this point in the history
Fix reflections

Signed-off-by: sinri <[email protected]>
  • Loading branch information
sinri committed Jun 18, 2024
1 parent 4cf061d commit f6bc3b2
Showing 4 changed files with 134 additions and 45 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@

<groupId>io.github.sinri</groupId>
<artifactId>Keel</artifactId>
<!-- <version>3.2.12-SNAPSHOT</version>-->
<version>3.2.12</version>
<!-- <version>3.2.12.1-SNAPSHOT</version>-->
<version>3.2.12.1</version>

<name>Keel</name>
<description>
109 changes: 84 additions & 25 deletions src/main/java/io/github/sinri/keel/helper/KeelFileHelper.java
Original file line number Diff line number Diff line change
@@ -4,13 +4,13 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSource;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -63,7 +63,18 @@ static KeelFileHelper getInstance() {
* @return the URL of target file; if not there, null return.
*/
@Nullable
@Deprecated(forRemoval = true, since = "3.2.12.1")
public URL getUrlOfFileInJar(@Nonnull String filePath) {
return getUrlOfFileInRunningJar(filePath);
}

/**
* @param filePath path string of the target file, or directory
* @return the URL of target file; if not there, null return.
* @since 3.2.12.1 original name is `getUrlOfFileInJar`.
*/
@Nullable
public URL getUrlOfFileInRunningJar(@Nonnull String filePath) {
return KeelFileHelper.class.getClassLoader().getResource(filePath);
}

@@ -73,8 +84,21 @@ public URL getUrlOfFileInJar(@Nonnull String filePath) {
* @param root ends with '/'
* @return list of JarEntry
*/
@Deprecated(forRemoval = true, since = "3.2.12.1")
@Nonnull
public List<JarEntry> traversalInJar(@Nonnull String root) {
return traversalInRunningJar(root);
}

/**
* Seek in JAR, under the root (exclusive)
*
* @param root ends with '/'
* @return list of JarEntry
* @since 3.2.12.1 original name is `traversalInJar`.
*/
@Nonnull
public List<JarEntry> traversalInRunningJar(@Nonnull String root) {
List<JarEntry> jarEntryList = new ArrayList<>();
try {
// should root ends with '/'?
@@ -121,44 +145,51 @@ public Future<String> crateTempFile(@Nullable String prefix, @Nullable String su

/**
* @since 3.2.11
* @since 3.2.12.1 Changed the implementation with checking class paths.
* Check if this process is running with JAR file.
*/
public boolean isRunningFromJAR() {
CodeSource src = this.getClass().getProtectionDomain().getCodeSource();
if (src == null) {
throw new RuntimeException();
}
URL location = src.getLocation();
if (location == null) {
throw new RuntimeException();
List<String> classPathList = getClassPathList();
for (var classPath : classPathList) {
if (!classPath.endsWith(".jar")) {
return false;
}
}
// System.out.println("src.getLocation: "+location.toString());
return location.toString().endsWith(".jar");

// ZipInputStream zip = new ZipInputStream(jar.openStream());
// while (true) {
// ZipEntry e = zip.getNextEntry();
// if (e == null)
// break;
// String name = e.getName();
// if (name.startsWith("path/to/your/dir/")) {
// /* Do something with this entry. */
// }
// }
return true;
}

/**
* @since 3.2.12.1
*/
public List<String> getClassPathList() {
String classpath = System.getProperty("java.class.path");
String[] classpathEntries = classpath.split(File.pathSeparator);
return new ArrayList<>(Arrays.asList(classpathEntries));
}

/**
* The in-class classes, i.e. subclasses, would be neglected.
*
* @since 3.2.11
*/
@Deprecated(since = "3.2.12.1", forRemoval = true)
public Set<String> seekPackageClassFilesInJar(@Nonnull String packageName) {
return seekPackageClassFilesInRunningJar(packageName);
}

/**
* The in-class classes, i.e. subclasses, would be neglected.
*
* @since 3.2.12.1 original name is `seekPackageClassFilesInJar`.
*/
public Set<String> seekPackageClassFilesInRunningJar(@Nonnull String packageName) {
Set<String> classes = new HashSet<>();
// Get the current class's class loader
ClassLoader classLoader = this.getClass().getClassLoader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// Get the URL of the JAR file containing the current class
URL jarUrl = classLoader.getResource(getClass().getName().replace('.', '/') + ".class");
String currentClassUrlInJarFile = getClass().getName().replace('.', '/') + ".class";
URL jarUrl = classLoader.getResource(currentClassUrlInJarFile);

if (jarUrl != null && jarUrl.getProtocol().equals("jar")) {
// Extract the JAR file path
@@ -176,17 +207,45 @@ public Set<String> seekPackageClassFilesInJar(@Nonnull String packageName) {
if (entryName.endsWith(".class")) {
// Convert the entry name to a fully qualified class name
String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
//System.out.println(className);
if (className.startsWith(packageName + ".") && !className.contains("$")) {
classes.add(className);
}
}
}
} catch (IOException e) {
Keel.getLogger().exception(e);
Keel.getLogger().debug(getClass() + " seekPackageClassFilesInRunningJar for package " + packageName + " error: " + e.getMessage());
}
}

return classes;
}

/**
* @param jarFile File built from JAR in class path.
* @since 3.2.12.1
*/
public List<String> traversalInJarFile(File jarFile) {
try (JarFile jar = new JarFile(jarFile)) {
List<String> list = new ArrayList<>();

Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (
entryName.endsWith(".class")
&& !entryName.contains("$")
&& !entryName.startsWith("META-INF")
) {
// 将路径形式的类名转换为 Java 类名
String className = entryName.replace("/", ".").replace(".class", "");
list.add(className);
}
}

return list;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
65 changes: 48 additions & 17 deletions src/main/java/io/github/sinri/keel/helper/KeelReflectionHelper.java
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -11,6 +12,7 @@
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static io.github.sinri.keel.facade.KeelInstance.Keel;
@@ -77,32 +79,42 @@ public <T extends Annotation> T[] getAnnotationsOfClass(@Nonnull Class<?> anyCla
* @param <R> the target base class to seek its implementations
* @return the sought classes in a set
* @since 3.0.6
* @since 3.2.12.1 rewrite
*/
public <R> Set<Class<? extends R>> seekClassDescendantsInPackage(@Nonnull String packageName, @Nonnull Class<R> baseClass) {
// Reflections reflections = new Reflections(packageName);
// return reflections.getSubTypesOf(baseClass);

if (Keel.fileHelper().isRunningFromJAR()) {
return seekClassDescendantsInPackageForJar(packageName, baseClass);
} else {
return seekClassDescendantsInPackageForFileSystem(packageName, baseClass);
Set<Class<? extends R>> set = new HashSet<>();

List<String> classPathList = Keel.fileHelper().getClassPathList();
for (String classPath : classPathList) {
if (classPath.endsWith(".jar")) {
Set<Class<? extends R>> classes = seekClassDescendantsInPackageForProvidedJar(classPath, packageName, baseClass);
set.addAll(classes);
} else {
Set<Class<? extends R>> classes = seekClassDescendantsInPackageForFileSystem(packageName, baseClass);
set.addAll(classes);
}
}

return set;
}

/**
* @since 3.2.11
*/
protected <R> Set<Class<? extends R>> seekClassDescendantsInPackageForFileSystem(@Nonnull String packageName, @Nonnull Class<R> baseClass) {
String packagePath = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<Class<? extends R>> descendantClasses = new HashSet<>();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// in file system
String packagePath = packageName.replace('.', '/');
try {
// Assuming classes are in a directory on the file system (e.g., not in a JAR)
URL resource = classLoader.getResource(packagePath);
if (resource != null) {
URI uri = resource.toURI();
Path startPath = Paths.get(uri);

Files.walkFileTree(startPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
@@ -112,14 +124,11 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO

try {
Class<? extends R> clazz = (Class<? extends R>) classLoader.loadClass(className);
if (
baseClass.isAssignableFrom(clazz)
//&& !baseClass.equals(clazz)
) {
if (baseClass.isAssignableFrom(clazz)) {
descendantClasses.add(clazz);
}
} catch (ClassNotFoundException e) {
Keel.getLogger().exception(e);
} catch (Throwable e) {
Keel.getLogger().debug(getClass() + " seekClassDescendantsInPackageForFileSystem for " + className + " error: " + e.getMessage());
}
}
return FileVisitResult.CONTINUE;
@@ -135,22 +144,44 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
/**
* @since 3.2.11
*/
protected <R> Set<Class<? extends R>> seekClassDescendantsInPackageForJar(@Nonnull String packageName, @Nonnull Class<R> baseClass) {
protected <R> Set<Class<? extends R>> seekClassDescendantsInPackageForRunningJar(@Nonnull String packageName, @Nonnull Class<R> baseClass) {
Set<Class<? extends R>> descendantClasses = new HashSet<>();
Set<String> strings = Keel.fileHelper().seekPackageClassFilesInJar(packageName);
Set<String> strings = Keel.fileHelper().seekPackageClassFilesInRunningJar(packageName);
for (String s : strings) {
try {
Class<?> aClass = Class.forName(s);
if (baseClass.isAssignableFrom(aClass)) {
descendantClasses.add((Class<? extends R>) aClass);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (Throwable e) {
Keel.getLogger().debug(getClass() + " seekClassDescendantsInPackageForRunningJar for " + s + " error: " + e.getMessage());
}
}
return descendantClasses;
}

/**
* @since 3.2.11
*/
protected <R> Set<Class<? extends R>> seekClassDescendantsInPackageForProvidedJar(@Nonnull String jarInClassPath, @Nonnull String packageName, @Nonnull Class<R> baseClass) {
Set<Class<? extends R>> descendantClasses = new HashSet<>();
List<String> classNames = Keel.fileHelper().traversalInJarFile(new File(jarInClassPath));
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
classNames.forEach(className -> {
if (className.startsWith(packageName + ".")) {
try {
Class<? extends R> clazz = (Class<? extends R>) classLoader.loadClass(className);
if (baseClass.isAssignableFrom(clazz)) {
descendantClasses.add(clazz);
}
} catch (Throwable e) {
Keel.getLogger().debug(getClass() + " seekClassDescendantsInPackageForProvidedJar for " + className + " error: " + e.getMessage());
}
}
});
return descendantClasses;
}

/**
* @return Whether the given `baseClass` is the base of the given `implementClass`.
* @since 3.0.10
Original file line number Diff line number Diff line change
@@ -64,7 +64,6 @@ public KeelWebReceptionistKit(Class<R> classOfReceptionist, Router router) {
public void loadPackage(String packageName) {
Set<Class<? extends R>> allClasses = Keel.reflectionHelper()
.seekClassDescendantsInPackage(packageName, classOfReceptionist);

try {
allClasses.forEach(this::loadClass);
} catch (Exception e) {

0 comments on commit f6bc3b2

Please sign in to comment.