I want enable logging in classes JarClassLoader and Menu.
In Menu class it works (log is printed to file and console). But logging doesn't work in JarClassLoader (logging does not work either in the console or in the file).
For simplicity, I added the message only to the class constructor of JarClassLoader and one message at the beginning of main(String[] args) method.
log4j2.properties
name=PropertiesConfig
property.filename=classloading/menu-module/logs
appenders=console, file
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
appender.file.type=File
appender.file.name=LOGFILE
appender.file.fileName=${filename}/propertieslogs.log
appender.file.layout.type=PatternLayout
appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
rootLogger.level=info
rootLogger.appenderRefs=stdout
rootLogger.appenderRef.stdout.ref=STDOUT
loggers=file
logger.file.name=com.example.classloading
logger.file.level=info
logger.file.appenderRefs=file
logger.file.appenderRef.file.ref=LOGFILE
JarClassLoader
package com.example.classloading;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JarClassLoader extends ClassLoader {
private static final Logger log = LogManager.getLogger(JarClassLoader.class);
private HashMap<String, Class<?>> cache = new HashMap<String, Class<?>>();
private String jarFileName;
private String packageName;
private static String WARNING = "Warning : No jar file found. Packet unmarshalling won't be possible. Please verify your classpath";
public JarClassLoader(String jarFileName, String packageName) {
log.info(">> inside JarClassLoader");
this.jarFileName = jarFileName;
this.packageName = packageName;
cacheClasses();
}
private void cacheClasses() {
try {
JarFile jarFile = new JarFile(jarFileName);
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) entries.nextElement();
// simple class validation based on package name
if (match(normalize(jarEntry.getName()), packageName)) {
byte[] classData = loadClassData(jarFile, jarEntry);
if (classData != null) {
Class<?> clazz = defineClass(stripClassName(normalize(jarEntry.getName())), classData, 0, classData.length);
cache.put(clazz.getName(), clazz);
System.out.println("== class " + clazz.getName() + " loaded in cache");
}
}
}
}
catch (IOException IOE) {
System.out.println(WARNING);
}
}
public synchronized Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> result = cache.get(name);
if (result == null)
result = cache.get(packageName + "." + name);
if (result == null)
result = super.findSystemClass(name);
System.out.println("== loadClass(" + name + ")");
return result;
}
private String stripClassName(String className) {
return className.substring(0, className.length() - 6);
}
private String normalize(String className) {
return className.replace('/', '.');
}
private boolean match(String className, String packageName) {
return className.startsWith(packageName) && className.endsWith(".class");
}
private byte[] loadClassData(JarFile jarFile, JarEntry jarEntry) throws IOException {
long size = jarEntry.getSize();
if (size == -1 || size == 0)
return null;
byte[] data = new byte[(int)size];
InputStream in = jarFile.getInputStream(jarEntry);
in.read(data);
return data;
}
}
Menu
package com.example.classloading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Scanner;
public class Menu {
private static final Logger log = LogManager.getLogger();
public static void main(String[] args) {
int select;
do {
log.info(">> menu started");
System.out.println("=== MENU ===");
System.out.println("This is a simple multi-module project that can dynamically load modules.");
System.out.println("1. Load and run simple-module");
System.out.println("2. Load and run module in jar from ...");
System.out.println("0. EXIT");
Scanner scanner = new Scanner(System.in);
select = scanner.nextInt();
switch (select) {
case 1: {
JarClassLoader jarClassLoader = new JarClassLoader("classloading/simple-module/target/simple-module-1.0-SNAPSHOT.jar", "com.example.classloading");
try {
Class<?> clas = jarClassLoader.loadClass("com.example.classloading.SimpleModule");
Module sample = (Module) clas.newInstance();
sample.run();
} catch (Exception e) {
e.printStackTrace();
}
break;
}
case 2: {
System.out.print(" Path to jar: ");
String jarFileName = scanner.next();
System.out.print(" Package name: ");
String packageName = scanner.next();
System.out.print(" Class to load and run: ");
String classToLoad = scanner.next();
JarClassLoader jarClassLoader = new JarClassLoader(jarFileName, packageName);
try {
Class<?> clas = jarClassLoader.loadClass(classToLoad);
Module sample = (Module) clas.newInstance();
sample.run();
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
} while (select != 0);
}
}
So, why does not logging work in the JarClassLoader class?
I have run your code and there is no problem with the logging. When I run the exact code, you have, the output is as follows;
[INFO ] 2017-04-09 20:07:15.208 [main] Menu - >> menu started
=== MENU ===
This is a simple multi-module project that can dynamically load modules.
1. Load and run simple-module
2. Load and run module in jar from ...
0. EXIT
1
[INFO ] 2017-04-09 20:07:20.681 [main] JarClassLoader - >> inside JarClassLoader
In your screenshot, I did not see that you have entered "1" or "2" in your console as the selection. As long as you do not enter anything from the console, the java program waits for an input and because of this reason, it never reaches to the line, in where logging is done.
Related
I am using JUL in my application. Normally, Netbeans opens an output tab that reads "Tomcat" and shows the logs I produce. It was working fine. But suddenly, I realise that my logs are not shown at all, only the System.out get printed. Not even the higuest LOG.log(Level.SEVERE, ".....
} catch(Exception e) {
System.out.println("This gets printed in Netbeans tab");
LOG.log(Level.SEVERE, "This doesnt");
}
I suspect it can be a library I included, which is messing with my logs. Is that possible at all? Can a librray change the way my logs are shown? How can I investigate this, since I am a bit lost?
I suspect it can be a library I included, which is messing with my logs. Is that possible at all?
Yes. The JUL to SLF4J Bridge can remove the console handler from the JUL root logger. Some libraries invoke LogManager.reset which can remove and close all handlers.
Can a library change the way my logs are shown?
Yes. Once the a log bridge is installed the JUL records are no longer formatted by the JUL formatters.
How can I investigate this, since I am a bit lost?
Modifying the logger tree requires permissions so you can install a SecurityManager with all permissions but then turn on debug tracing with -Djava.security.debug="access,stack" to determine the caller that is modifying the logger tree.
If that doesn't work you can use good ole' System.out to print the logger tree and the attached handlers before and after a library is loaded. Then start adding an removing libraries until you see the logger change.
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class DebugLogging {
private static final Logger log = Logger.getLogger("test");
public static void main(String[] a) {
log.log(Level.FINEST, "Finest");
log.log(Level.FINER, "FINER");
log.log(Level.FINE, "FINE");
log.log(Level.CONFIG, "CONFIG");
log.log(Level.INFO, "INFO");
log.log(Level.WARNING, "WARNING");
log.log(Level.SEVERE, "SEVERE");
log.finest("Finest Log");
log.finer("Finer Log");
log.fine("Fine Log");
log.config("Config Log");
log.info("Info Log");
log.warning("Warning Log");
log.severe("Severe Log");
printConfig(System.err);
}
private static void printConfig(PrintStream ps) {
String cname = System.getProperty("java.util.logging.config.class");
if (cname != null) {
try {
ClassLoader sys = ClassLoader.getSystemClassLoader();
Class<?> c = Class.forName(cname, false, sys);
ps.println(sys.getClass().getName() +" found log configuration class " + c.getName());
} catch (LinkageError | ClassNotFoundException | RuntimeException cnfe) {
ps.println("Unable to load " + cname);
cnfe.printStackTrace(ps);
}
} else {
ps.println("java.util.logging.config.class was null");
}
String file = System.getProperty("java.util.logging.config.file");
if (file != null) {
ps.println("java.util.logging.config.file=" + file);
try {
ps.println("CanonicalPath=" + new File(file).getCanonicalPath());
} catch (RuntimeException | IOException ioe) {
ps.println("Unable to resolve path for " + file);
ioe.printStackTrace(ps);
}
try {
Path p = Paths.get(file);
if (Files.isReadable(p)) {
ps.println(file + " is readable and has size " + Files.size(p));
} else {
if (Files.exists(p)) {
ps.println(file + " exists for " + System.getProperty("user.name") + " but is not readable.");
} else {
ps.println(file + " doesn't exist for " + System.getProperty("user.name"));
}
}
} catch (RuntimeException | IOException ioe) {
ps.println("Unable to read " + file);
ioe.printStackTrace(ps);
}
} else {
ps.println("java.util.logging.config.file was null");
}
LogManager lm = LogManager.getLogManager();
ps.append("LogManager=").println(lm.getClass().getName());
synchronized (lm) {
Enumeration<String> e = lm.getLoggerNames();
while (e.hasMoreElements()) {
Logger l = lm.getLogger(e.nextElement());
if (l != null) {
print(l, ps);
}
}
}
}
private static void print(Logger l, PrintStream ps) {
String scn = l.getClass().getSimpleName();
ps.append("scn=").append(scn).append(", n=").append(l.getName())
.append(", uph=").append(String.valueOf(l.getUseParentHandlers()))
.append(", l=").append(String.valueOf(l.getLevel()))
.append(", fl=").println(l.getFilter());
for (Handler h : l.getHandlers()) {
ps.append("\t").append(l.getName()).append("->")
.append(h.getClass().getName()).append(", h=")
.append(String.valueOf(h.getLevel())).append(", fl=")
.append(String.valueOf(h.getFilter())).println();
}
}
}
I have a Jar in java which is containing 2 classes and 1 Interface. How can i get the interface and class names from the jar. Currently I am able to get the class names, but not the interface name.
List jClasses = getClasseNames("D://Test.jar");
System.out.println(jClasses.size());
for (int i = 0; i < jClasses.size(); i++) {
System.out.println("Print Classes ::" + jClasses.get(i));
if(( null != jClasses.getClass().getInterfaces()[i])) {
System.out.println(jClasses.getClass().getInterfaces()[i]);
} else {
System.out.println("No connection");
}
}
public static List getClasseNames(String jarName) {
ArrayList classes = new ArrayList();
try {
JarInputStream jarFile = new JarInputStream(new FileInputStream(
jarName));
JarEntry jarEntry;
while (true) {
jarEntry = jarFile.getNextJarEntry();
if (jarEntry == null) {
break;
}
if (jarEntry.getName().endsWith(".class")) {
classes.add(jarEntry.getName().replaceAll("/", "\\."));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return classes;
}
output :
Print Classes ::com.java.testclient.PTest1.class
interface java.util.List
======
Print Classes ::com.java.testclient.ClassSpy.class
interface java.util.RandomAccess
======
Print Classes ::com.java.testclient.myInt.class
interface java.lang.Cloneable
======
Print Classes ::com.java.testclient.PTest.class
interface java.io.Serializable
Please suggest.
You can use this class:
package io.github.gabrielbb.java.utilities;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* #author Gabriel Basilio Brito
* #since 12/26/2016
* #version 1.1
*/
public class ClassesAndInterfacesFromJar {
public static List<Class> getJarClasses(String jarPath) throws IOException, ClassNotFoundException {
File jarFile = new File(jarPath);
return getJarClasses(jarFile);
}
public static List<Class> getJarClasses(File jar) throws IOException, ClassNotFoundException {
ArrayList<Class> classes = new ArrayList();
JarInputStream jarInputStream = null;
URLClassLoader cl;
try {
cl = URLClassLoader.newInstance(new URL[]{new URL("jar:file:" + jar + "!/")}); // To load classes inside the jar, after getting their names
jarInputStream = new JarInputStream(new FileInputStream(
jar)); // Getting a JarInputStream to iterate through the Jar files
JarEntry jarEntry = jarInputStream.getNextJarEntry();
while (jarEntry != null) {
if (jarEntry.getName().endsWith(".class")) { // Avoiding non ".class" files
String className = jarEntry.getName().replaceAll("/", "\\."); // The ClassLoader works with "." instead of "/"
className = className.substring(0, jarEntry.getName().length() - 6); // Removing ".class" from the string
Class clazz = cl.loadClass(className); // Loading the class by its name
classes.add(clazz);
}
jarEntry = jarInputStream.getNextJarEntry(); // Next File
}
} finally {
if (jarInputStream != null) {
jarInputStream.close(); // Closes the FileInputStream
}
}
return classes;
}
// Main Method for testing purposes
public static void main(String[] args) {
try {
String jarPath = "C://Test.jar";
List<Class> classes = getJarClasses(jarPath);
for (Class c : classes) {
// Here we can use the "isInterface" method to differentiate an Interface from a Class
System.out.println(c.isInterface() ? "Interface: " + c.getName() : "Class: " + c.getName());
}
} catch (Exception ex) {
System.err.println(ex);
}
}
It can be found at:
https://github.com/GabrielBB/Java-Utilities/blob/master/ClassesAndInterfacesFromJar.java
I wrote my classloader:
package ru.sberbank.school.homework8;
import ru.sberbank.school.homework8.plugin.Plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class PluginManager extends ClassLoader {
private final String pluginRootDirectory;
public PluginManager(String pluginRootDirectory) {
this.pluginRootDirectory = pluginRootDirectory;
}
public Plugin load(String pluginName, String pluginClassName) {
String name = pluginName + "." + pluginClassName;
try {
Class clazz;
try {
clazz = super.findSystemClass(name);
} catch (ClassNotFoundException e) {
String fileName = pluginRootDirectory + "\\" + pluginName + "\\" + pluginClassName + ".class";
try (FileInputStream fin = new FileInputStream(fileName)) {
byte[] buffer = new byte[(int) (new File(fileName).length())];
fin.read(buffer);
clazz = defineClass(name, buffer, 0, buffer.length);
}
}
return (Plugin)clazz.newInstance();
} catch (IOException | InstantiationException | IllegalAccessException ignored) {
return null;
}
}
}
When I run it:
package ru.sberbank.school.homework8;
import ru.sberbank.school.homework8.plugin.Plugin;
public class PluginManagerTest {
public static void main(String[] args) {
String pluginRootDirectory = "D:\\sbt\\target\\classes\\ru\\sberbank\\school\\homework8";
PluginManager pluginManager = new PluginManager(pluginRootDirectory);
Plugin plugin = pluginManager.load("plugin", "PluginImpl");
if (plugin != null) {
plugin.doUseful();
}
}
}
Exception in thread "main" java.lang.NoClassDefFoundError:
plugin/PluginImpl (wrong name:
ru/sberbank/school/homework8/plugin/PluginImpl) at
java.lang.ClassLoader.defineClass1(Native Method)
I get NoClassDefFoundError. Why??? How can I fix it???
Help me, please!
package ru.sberbank.school.homework8.plugin;
public class PluginImpl implements Plugin {
#Override
public void doUseful() {
System.out.println("My plugin!");
}
}
You get this error because you don't provide the correct FQN of your class, indeed in your load method, you try to find the class corresponding to pluginName + "." + pluginClassName that will be in your case plugin.PluginImpl but the package name of your class PluginImpl is actually ru.sberbank.school.homework8.plugin such that the real FQN of your class is ru.sberbank.school.homework8.plugin.PluginImpl.
To fix this problem, you need to replace:
Plugin plugin = pluginManager.load("plugin", "PluginImpl");
With:
Plugin plugin = pluginManager.load("ru.sberbank.school.homework8.plugin", "PluginImpl");
Or you could modify your method load to add a prefix assuming that you will always retrieve your plugins from the same root package:
public Plugin load(String pluginName, String pluginClassName) {
String name = "ru.sberbank.school.homework8." + pluginName + "." + pluginClassName;
I've been trying to search the internet, but it seems I cannot find a library for helping processing of Annotations in a POJO. Is there any that exist?
Currently we can process this through code like this:
// Get id
Object id = null;
for (Field field : obj.getClass().getDeclaredFields()){
String fieldName = field.getName();
Object fieldValue = field.get(obj);
if (field.isAnnotationPresent(Id.class)){
id = fieldValue;
}
}
Is there a library to help quickly process annotation and with the associated value.
Take a look at How do I read all classes from a Java package in the classpath?
I was using https://code.google.com/p/reflections/, and now switched to this
package com.clemble.test.reflection;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class AnnotationReflectionUtils {
/** URL prefix for loading from the file system: "file:" */
public static final String FILE_URL_PREFIX = "file:";
/** URL protocol for an entry from a jar file: "jar" */
public static final String URL_PROTOCOL_JAR = "jar";
/** URL protocol for an entry from a zip file: "zip" */
public static final String URL_PROTOCOL_ZIP = "zip";
/** URL protocol for an entry from a JBoss jar file: "vfszip" */
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
/** URL protocol for a JBoss VFS resource: "vfs" */
public static final String URL_PROTOCOL_VFS = "vfs";
/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
public static final String URL_PROTOCOL_WSJAR = "wsjar";
/** URL protocol for an entry from an OC4J jar file: "code-source" */
public static final String URL_PROTOCOL_CODE_SOURCE = "code-source";
/** Separator between JAR URL and file path within the JAR */
public static final String JAR_URL_SEPARATOR = "!/";
// Taken from https://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath
public static <T extends Annotation> List<Class<?>> findCandidates(String basePackage, Class<T> searchedAnnotation) {
ArrayList<Class<?>> candidates = new ArrayList<Class<?>>();
Enumeration<URL> urls;
String basePath = basePackage.replaceAll("\\.", File.separator);
try {
urls = Thread.currentThread().getContextClassLoader().getResources(basePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (isJarURL(url)) {
try {
candidates.addAll(doFindPathMatchingJarResources(url, basePath, searchedAnnotation));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
File directory = new File(url.getFile());
if (directory.exists() && directory.isDirectory()) {
for (File file : new File(url.getFile()).listFiles())
fetchCandidates(basePackage, file, searchedAnnotation, candidates);
}
}
}
return candidates;
}
private static <T extends Annotation> void fetchCandidates(String basePackage, File candidate, Class<T> searchedAnnotation, List<Class<?>> candidates) {
if (candidate.isDirectory()) {
for (File file : candidate.listFiles())
fetchCandidates(basePackage + "." + candidate.getName(), file, searchedAnnotation, candidates);
} else {
String fileName = candidate.getName();
if (fileName.endsWith(".class")) {
String className = fileName.substring(0, fileName.length() - 6);
Class<?> foundClass = checkCandidate(basePackage + "." + className, searchedAnnotation);
if (foundClass != null)
candidates.add(foundClass);
}
}
}
public static boolean isJarURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol) || (URL_PROTOCOL_CODE_SOURCE
.equals(protocol) && url.getPath().contains(JAR_URL_SEPARATOR)));
}
public static <T extends Annotation> Class<?> checkCandidate(String className, Class<T> searchedAnnotation) {
try {
Class<?> candidateClass = Class.forName(className);
Target target = searchedAnnotation.getAnnotation(Target.class);
for(ElementType elementType: target.value()) {
switch(elementType) {
case TYPE:
if (candidateClass.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case CONSTRUCTOR:
for(Constructor<?> constructor: candidateClass.getConstructors())
if(constructor.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case METHOD:
for(Method method: candidateClass.getMethods())
if(method.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case FIELD:
for(Field field: candidateClass.getFields())
if(field.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
default:
break;
}
}
} catch (ClassNotFoundException e) {
} catch (NoClassDefFoundError e) {
}
return null;
}
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style PathMatcher.
*
* #param rootDirResource the root directory as Resource
* #param subPattern the sub pattern to match (below the root directory)
* #return the Set of matching Resource instances
* #throws IOException in case of I/O errors
* #see java.net.JarURLConnection
* #see org.springframework.util.PathMatcher
*/
protected static <T extends Annotation> Set<Class<?>> doFindPathMatchingJarResources(URL sourceUrl, String basePackage, Class<T> searchedAnnotation)
throws IOException {
URLConnection con = sourceUrl.openConnection();
JarFile jarFile;
String jarFileUrl;
String rootEntryPath;
boolean newJarFile = false;
if (con instanceof JarURLConnection) {
// Should usually be the case for traditional JAR files.
JarURLConnection jarCon = (JarURLConnection) con;
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
} else {
// No JarURLConnection -> need to resort to URL file parsing.
// We'll assume URLs of the format "jar:path!/entry", with the protocol
// being arbitrary as long as following the entry format.
// We'll also handle paths with and without leading "file:" prefix.
String urlFile = sourceUrl.getFile();
int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
jarFileUrl = urlFile.substring(0, separatorIndex);
rootEntryPath = urlFile.substring(separatorIndex + JAR_URL_SEPARATOR.length());
jarFile = getJarFile(jarFileUrl);
} else {
jarFile = new JarFile(urlFile);
jarFileUrl = urlFile;
rootEntryPath = "";
}
newJarFile = true;
}
try {
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
// Root entry path must end with slash to allow for proper matching.
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Class<?>> result = new LinkedHashSet<Class<?>>(8);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.startsWith(rootEntryPath) && entryPath.endsWith(".class")) {
int entryLength = entryPath.length();
String className = entryPath.replaceAll(File.separator, ".").substring(0, entryLength - 6);
Class<?> foundClass = checkCandidate(className, searchedAnnotation);
if (foundClass != null)
result.add(foundClass);
}
}
return result;
} finally {
// Close jar file, but only if freshly obtained -
// not from JarURLConnection, which might cache the file reference.
if (newJarFile) {
jarFile.close();
}
}
}
/**
* Resolve the given jar file URL into a JarFile object.
*/
protected static JarFile getJarFile(String jarFileUrl) throws IOException {
if (jarFileUrl.startsWith(FILE_URL_PREFIX)) {
try {
return new JarFile(new URI(jarFileUrl.replaceAll(" ", "%20")).getSchemeSpecificPart());
} catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new JarFile(jarFileUrl.substring(FILE_URL_PREFIX.length()));
}
} else {
return new JarFile(jarFileUrl);
}
}
}
This is based on some Spring utility, class which I could not use directly in my application, but I forgot which one was it.
I've written simple ReflectionUtils, to find all classes with specific Annotation, I am using it successfully on my server, but for some reason it does not perform as expected on Android. (I specifically use it to find all classes annotated with #JsonTypeName, and add them to ObjectMapper context)
What might be the problem?
package com.acme.reflection.utils;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ReflectionUtils {
/** URL prefix for loading from the file system: "file:" */
public static final String FILE_URL_PREFIX = "file:";
/** URL protocol for an entry from a jar file: "jar" */
public static final String URL_PROTOCOL_JAR = "jar";
/** URL protocol for an entry from a zip file: "zip" */
public static final String URL_PROTOCOL_ZIP = "zip";
/** URL protocol for an entry from a JBoss jar file: "vfszip" */
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
/** URL protocol for a JBoss VFS resource: "vfs" */
public static final String URL_PROTOCOL_VFS = "vfs";
/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
public static final String URL_PROTOCOL_WSJAR = "wsjar";
/** URL protocol for an entry from an OC4J jar file: "code-source" */
public static final String URL_PROTOCOL_CODE_SOURCE = "code-source";
/** Separator between JAR URL and file path within the JAR */
public static final String JAR_URL_SEPARATOR = "!/";
// Taken from http://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath
public static <T extends Annotation> List<Class<?>> findCandidates(String basePackage, Class<T> searchedAnnotation) {
ArrayList<Class<?>> candidates = new ArrayList<Class<?>>();
Enumeration<URL> urls;
String basePath = basePackage.replaceAll("\\.", File.separator);
try {
urls = Thread.currentThread().getContextClassLoader().getResources(basePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (isJarURL(url)) {
try {
candidates.addAll(doFindPathMatchingJarResources(url, basePath, searchedAnnotation));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
File directory = new File(url.getFile());
if (directory.exists() && directory.isDirectory()) {
for (File file : new File(url.getFile()).listFiles())
fetchCandidates(basePackage, file, searchedAnnotation, candidates);
}
}
}
return candidates;
}
private static <T extends Annotation> void fetchCandidates(String basePackage, File candidate, Class<T> searchedAnnotation, List<Class<?>> candidates) {
if (candidate.isDirectory()) {
for (File file : candidate.listFiles())
fetchCandidates(basePackage + "." + candidate.getName(), file, searchedAnnotation, candidates);
} else {
String fileName = candidate.getName();
if (fileName.endsWith(".class")) {
String className = fileName.substring(0, fileName.length() - 6);
Class<?> foundClass = checkCandidate(basePackage + "." + className, searchedAnnotation);
if (foundClass != null)
candidates.add(foundClass);
}
}
}
public static boolean isJarURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol) || (URL_PROTOCOL_CODE_SOURCE
.equals(protocol) && url.getPath().contains(JAR_URL_SEPARATOR)));
}
public static <T extends Annotation> Class<?> checkCandidate(String className, Class<T> searchedAnnotation) {
try {
Class<?> candidateClass = Class.forName(className);
Target target = searchedAnnotation.getAnnotation(Target.class);
for(ElementType elementType: target.value()) {
switch(elementType) {
case TYPE:
if (candidateClass.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case CONSTRUCTOR:
for(Constructor<?> constructor: candidateClass.getConstructors())
if(constructor.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case METHOD:
for(Method method: candidateClass.getMethods())
if(method.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
case FIELD:
for(Field field: candidateClass.getFields())
if(field.getAnnotation(searchedAnnotation) != null)
return candidateClass;
break;
default:
break;
}
}
} catch (ClassNotFoundException | NoClassDefFoundError e) {
;
}
return null;
}
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style PathMatcher.
*
* #param rootDirResource the root directory as Resource
* #param subPattern the sub pattern to match (below the root directory)
* #return the Set of matching Resource instances
* #throws IOException in case of I/O errors
* #see java.net.JarURLConnection
* #see org.springframework.util.PathMatcher
*/
protected static <T extends Annotation> Set<Class<?>> doFindPathMatchingJarResources(URL sourceUrl, String basePackage, Class<T> searchedAnnotation)
throws IOException {
URLConnection con = sourceUrl.openConnection();
JarFile jarFile;
String jarFileUrl;
String rootEntryPath;
boolean newJarFile = false;
if (con instanceof JarURLConnection) {
// Should usually be the case for traditional JAR files.
JarURLConnection jarCon = (JarURLConnection) con;
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
} else {
// No JarURLConnection -> need to resort to URL file parsing.
// We'll assume URLs of the format "jar:path!/entry", with the protocol
// being arbitrary as long as following the entry format.
// We'll also handle paths with and without leading "file:" prefix.
String urlFile = sourceUrl.getFile();
int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
jarFileUrl = urlFile.substring(0, separatorIndex);
rootEntryPath = urlFile.substring(separatorIndex + JAR_URL_SEPARATOR.length());
jarFile = getJarFile(jarFileUrl);
} else {
jarFile = new JarFile(urlFile);
jarFileUrl = urlFile;
rootEntryPath = "";
}
newJarFile = true;
}
try {
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
// Root entry path must end with slash to allow for proper matching.
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Class<?>> result = new LinkedHashSet<>(8);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.startsWith(rootEntryPath) && entryPath.endsWith(".class")) {
int entryLength = entryPath.length();
String className = entryPath.replaceAll(File.separator, ".").substring(0, entryLength - 6);
Class<?> foundClass = checkCandidate(className, searchedAnnotation);
if (foundClass != null)
result.add(foundClass);
}
}
return result;
} finally {
// Close jar file, but only if freshly obtained -
// not from JarURLConnection, which might cache the file reference.
if (newJarFile) {
jarFile.close();
}
}
}
/**
* Resolve the given jar file URL into a JarFile object.
*/
protected static JarFile getJarFile(String jarFileUrl) throws IOException {
if (jarFileUrl.startsWith(FILE_URL_PREFIX)) {
try {
return new JarFile(new URI(jarFileUrl.replaceAll(" ", "%20")).getSchemeSpecificPart());
} catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new JarFile(jarFileUrl.substring(FILE_URL_PREFIX.length()));
}
} else {
return new JarFile(jarFileUrl);
}
}
}
Found a simmilar question on:
Implementing Spring-like package scanning in Android
After some consideration, I decided to change the approach for ObjectManager.
I keep module configurations in predefined package xxx.yyy.zzz.json.AAAJsonModule and on ObjectMapper construction try to load module configurations in xxx.yyy.zzz.json.{AAA}JsonModule modules, if module is missing, I ignore it. So that way I can dynamically change ObjectMapper mapping, based on the present jars in classpath.