I'm not sure if I'm using the correct terminology here.. but if my package name is set up like this:
com.example.fungame
-ClassA
-ClassB
-com.example.fungame.sprite
-ClassC
-ClassD
How can I programmatically get an array (a Class[] I'm guessing) of all the classes in the .sprite subdirectory?
Try this method:
public static Class[] getClasses(String pckgname) throws ClassNotFoundException {
ArrayList classes=new ArrayList();
File directory = null;
try {
directory = new File(Thread.currentThread().getContextClassLoader().getResource(pckgname.replace('.', '/')).getFile());
} catch(NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package");
}
if (directory.exists()) {
// Get the list of the files contained in the package
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
// we are only interested in .class files
if(files[i].endsWith(".class")) {
// removes the .class extension
try {
Class cl = Class.forName(pckgname + '.' + files[i].substring(0, files[i].length() - 6));
classes.add(cl);
} catch (ClassNotFoundException ex) {
}
}
}
} else {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package");
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}
Related
i have a mehod getAllTests() that load external jar in a folder and with Reflection search method with Annotation Test (#Test).
I use:
URLClassLoader class for load Jar File;
Class currentClass=child.loadClass(classname); for load class;
Method method = currentClass.getMethods()[i];
Annotation annTest = method.getAnnotation(Test.class);
for get Method and get Annotation.
My code is this:
public static void getAllTests() throws IllegalArgumentException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException,LinkageError, ClassNotFoundException {
TestLoaderApplication.testClassObjMap.clear(); <--is a HashMap class
LoadLibrary loadLibrary=new LoadLibrary();<--used for search all file .jar in folder
List<JarFile> jarList= loadLibrary.getListJar(pathJars).stream().map(f -> {
try {
return new JarFile(f);
} catch (IOException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList());
for (JarFile j : jarList) {
URLClassLoader child = new URLClassLoader(
new URL[] {new File(j.getName()).toURI().toURL()},
ServiceUtil.class.getClassLoader()
);
for (Enumeration<JarEntry> entries = j.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String file = entry.getName();
if (file.endsWith(".class")) {
String classname = file.replaceAll("/", ".")
.substring(0, file.lastIndexOf("."));
try {
Class currentClass=child.loadClass(classname);
List<String> testMethods = new ArrayList<>();
for (int i = 0; i < currentClass.getMethods().length; i++) {
Method method = currentClass.getMethods()[i];
Annotation annTest = method.getAnnotation(Test.class);
Annotation annTestFactory = method.getAnnotation(TestFactory.class);
if (annTest != null || annTestFactory != null) {
testMethods.add(method.getName());
}
}//fine for metodi
if (testMethods.size() >=1) {
testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("\\")+1),classname,testMethods);
TestLoaderApplication.testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("/")+1),classname,testMethods);
LOGGER.info(String.format("%s %s %s",j.toString(),classname,testMethods));
}
}catch (NoClassDefFoundError e) {
LOGGER.warn("WARNING: failed NoClassDefFoundError " + classname + " from " + file);
}
catch (Throwable e) {
LOGGER.warn("WARNING: failed to instantiate " + classname + " from " + file);
}
}//if .class
}//chiudo jarentry for
j.close();
child.close();
child=null;
}//chiudo jarfile for
System.gc();
LOGGER.info("Test Loader Console Back-End:\tFine Reflection Scan");
}
At end of this function i close both JarFile(j) and classLoader(child) and set child=null and agter call garbage collector.
But if i call this method many times at end i have about 100% memory used by java webapps.
This method is in a spring-boot project and i use Tomcat for webserver.
Can you recommend some optimization in the use of the classLoader and if this is the correct way to look for methods with annotation #Test in Java?
Thanks
I want take all mehod with #Test in all JAR file with many class.
i have my main in src/main/java/it/anas/testsuiteloader/controller.java
and jar file in src/main/java/test/* <---test is a package
my code is:
//TAKE ALL JAR FILE
List<JarFile> jarList = Files.walk(Paths.get("src/main/java/test"))
.filter(f -> f.toString().endsWith(".jar"))
.map(Path::toFile).map(f -> {
try {
return new JarFile(f);
} catch (IOException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList());
//in all jar, in all class in all method
for (JarFile j : jarList) {
for (Enumeration<JarEntry> entries = j.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String file = entry.getName();
if (file.endsWith(".class")) {
String classname = file.replaceAll("/", ".")
.substring(0, file.lastIndexOf("."));
try {
Class<?> currentClass = Class.forName(classname); <----ERROR!!!!
List<String> testMethods = new ArrayList<>();
for (int i = 0; i < currentClass.getMethods().length; i++) {
Method method = currentClass.getMethods()[i];
Annotation ann = method.getAnnotation(Test.class);
if (ann != null) {
testMethods.add(method.getName());
}
}//fine for metodi
if (testMethods.size() >1) {
testClassObjList.put(j.toString(),classname,testMethods);
System.out.format("%s %s %s",j.toString(),classname,testMethods);
}
} catch (Throwable e) {System.out.println("WARNING: failed to instantiate " + classname + " from " + file); }
}
}
It take correct class name:
String classname = file.replaceAll("/", ".")
.substring(0, file.lastIndexOf(".")); //<----- IS OK i view here class name
But here i have error:
Class<?> currentClass = Class.forName(classname); //<-----ERROR no class found!
Some TIPS???
How i search class in JAR file??
thanks
Regards
You open a jar file (jar is an archive in zip format) and iterate over the entries in the archive. Your problem is that you never loaded your jar to the JVM, so Class.forName will fail.
You need to load your jar file dynamically.
Here is my code for finding annotations in one class for dynamically loaded jar, inspired by How to load JAR files dynamically at Runtime?
var url = new File("YOUR_JAR.jar").toURI().toURL();
var child = new URLClassLoader(
new URL[] {url},
this.getClass().getClassLoader()
);
var classToLoad = Class.forName("YOUR_CLASS_FULLY_QUALIFIED_NAME", true, child);
var methods = classToLoad.getMethods();
for (var method: methods) {
Annotation ann = method.getAnnotation(Test.class);
if (ann != null) {
System.out.println("method: " + method.getName());
}
}
You can easily extend that to process multiple classes and multiple jars.
Good day!
I have a method which returns me an array of report names
System.out.println(bc[i].getDefaultName().getValue()
i want to use array output in other class, how i need to linked method outpud in my array in other class?
Method is:
public class ReoprtSearch {
public void executeTasks() {
PropEnum props[] = new PropEnum[] { PropEnum.searchPath, PropEnum.defaultName};
BaseClass bc[] = null;
String searchPath = "//report";
//searchPath for folder - //folder, report - //report, folder and report - //folder | //report
try {
SearchPathMultipleObject spMulti = new SearchPathMultipleObject(searchPath);
bc = cmService.query(spMulti, props, new Sort[] {}, new QueryOptions());
} catch (Exception e) {
e.printStackTrace();
return;
}
if (bc != null) {
for (int i = 0; i < bc.length; i++) {
System.out.println(bc[i].getDefaultName().getValue();
}
}
}
}
array in what i want put the array looks like:
String [] folders =
my trying like:
ReoprtSearch search = new ReoprtSearch();
String [] folders = {search.executeTasks()};
Returns me an error: cannot convert from void to string
Give me an explanations to understand how i can related to method output from other class.
Thanks
The problem is that your executeTasks method doesn't actually return anything (which is why it's void), and just prints to stdout. Instead of printing, add the names to an array and then return it. Something like this:
public class ReoprtSearch {
public String[] executeTasks() {
PropEnum props[] = new PropEnum[] { PropEnum.searchPath, PropEnum.defaultName};
BaseClass bc[] = null;
String searchPath = "//report";
//searchPath for folder - //folder, report - //report, folder and report - //folder | //report
try {
SearchPathMultipleObject spMulti = new SearchPathMultipleObject(searchPath);
bc = cmService.query(spMulti, props, new Sort[] {}, new QueryOptions());
} catch (Exception e) {
e.printStackTrace();
return null;
}
if (bc != null) {
String results[] = new String[bc.length];
for (int i = 0; i < bc.length; i++) {
results[i] = bc[i].getDefaultName().getValue();
}
return results;
}
return null;
}
}
Sorry I'm new here but I have and issue I'm hoping someone can help me solve.
This code runs perfect while in eclipse, but after compiled it say's:
java.lang.IllegalArgumentException: URI is not hierarchical
Any help would be appropriated, thanks!
public void loadMods(String pkg) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
List<Class<?>> classes = getClasses(pkg);
for(Class<?> c : classes) {
for (Method m : c.getMethods()) {
Object o = null;
o = c.newInstance();
if (m.getName().contains("load")) {
m.setAccessible(true);
m.invoke(o);
}
}
}
}
public static List<Class<?>> getClasses(String pkg) {
String pkgname = pkg;
List<Class<?>> classes = new ArrayList<Class<?>>();
File directory = null;
String fullPath;
String relPath = pkgname.replace('.', '/');
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
if (resource == null) {
throw new RuntimeException("No resource for " + relPath);
}
fullPath = resource.getFile();
try {
directory = new File(resource.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(pkgname + " (" + resource + ") invalid URL / URI.", e);
} catch (IllegalArgumentException e) {
directory = null;
}
if (directory != null && directory.exists()) {
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
if (files[i].endsWith(".class")) {
String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
try {
classes.add(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new RuntimeException("ClassNotFoundException loading " + className);
}
} else {
String pkgnamex = pkgname + '.' + files[i];
List<Class<?>> classesx = new ArrayList<Class<?>>();
File directoryx = null;
String fullPathx;
String relPathx = pkgnamex.replace('.', '/');
URL resourcex = ClassLoader.getSystemClassLoader().getResource(relPathx);
if (resourcex == null) {
throw new RuntimeException("No resource for " + relPathx);
}
fullPathx = resourcex.getFile();
try {
directoryx = new File(resourcex.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(pkgnamex + " (" + resourcex + ") invalid URL / URI.", e);
} catch (IllegalArgumentException e) {
directoryx = null;
}
if (directoryx != null && directoryx.exists()) {
String[] filesx = directoryx.list();
for (int ix = 0; ix < filesx.length; ix++) {
if (filesx[ix].endsWith(".class")) {
String classNamex = pkgnamex + '.' + filesx[ix].substring(0, filesx[ix].length() - 6);
try {
classes.add(Class.forName(classNamex));
} catch (ClassNotFoundException e) {
throw new RuntimeException("ClassNotFoundException loading " + classNamex);
}
}
}
}
}
}
}
return classes;
}
When you run the code from within Eclipse it uses the compiled classes (by default in folder 'target'). However if you run the code from external normally you use a JAR file created by Eclipse.
And this problem arises when referencing something inside the JAR which is explained by the linked questions.
In short: URIs in the file system are syntactically correct. An URI referencing something into a JAR is no more a valid URI.
I have the following code to iterate over folders and files in the class path and determine the classes and get a field with a ID and print them out to a logger. This is working fine if I run this code in my IDE, but if I package my project into a JAR file and this JAR file into a EXE file with launch4j, I can't iterate over my classes again.
I get the following path if I try to iterate over my classes in the JAR/EXE file:
file:/C:/ENTWICKLUNG/java/workspaces/MyProject/MyProjectTest/MyProjectSNAPSHOT.exe!/com/abc/def
How can I achieve this to iterate over all my classes in my JAR/EXE file?
public class ClassInfoAction extends AbstractAction
{
/**
* Revision/ID of this class from SVN/CVS.
*/
public static String ID = "#(#) $Id ClassInfoAction.java 43506 2013-06-27 10:23:39Z $";
private ClassLoader classLoader = ClassLoader.getSystemClassLoader();
private ArrayList<String> classIds = new ArrayList<String>();
private ArrayList<String> classes = new ArrayList<String>();
private int countClasses = 0;
#Override
public void actionPerformed(ActionEvent e)
{
countClasses = 0;
classIds = new ArrayList<String>();
classes = new ArrayList<String>();
getAllIds();
Iterator<String> it = classIds.iterator();
while (it.hasNext())
{
countClasses++;
//here I print out the ID
}
}
private void getAllIds()
{
String tempName;
String tempAbsolutePath;
try
{
ArrayList<File> fileList = new ArrayList<File>();
Enumeration<URL> roots = ClassLoader.getSystemResources("com"); //it is a path like com/abc/def I won't do this path public
while (roots.hasMoreElements())
{
URL temp = roots.nextElement();
fileList.add(new File(temp.getPath()));
GlobalVariables.LOGGING_logger.info(temp.getPath());
}
for (int i = 0; i < fileList.size(); i++)
{
for (File file : fileList.get(i).listFiles())
{
LinkedList<File> newFileList = null;
if (file.isDirectory())
{
newFileList = (LinkedList<File>) FileUtils.listFiles(file, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
if (newFileList != null)
{
for (int j = 0; j < newFileList.size(); j++)
{
tempName = newFileList.get(j).getName();
tempAbsolutePath = newFileList.get(j).getAbsolutePath();
checkIDAndAdd(tempName, tempAbsolutePath);
}
}
}
else
{
tempName = file.getName();
tempAbsolutePath = file.getAbsolutePath();
checkIDAndAdd(tempName, tempAbsolutePath);
}
}
}
getIdsClasses();
}
catch (IOException e)
{
}
}
private void checkIDAndAdd(String name, String absolutePath)
{
if (name.endsWith(".class") && !name.matches(".*\\d.*") && !name.contains("$"))
{
String temp = absolutePath.replace("\\", ".");
temp = temp.substring(temp.lastIndexOf(/* Class prefix */)); //here I put in the class prefix
classes.add(FilenameUtils.removeExtension(temp));
}
}
private void getIdsClasses()
{
for (int i = 0; i < classes.size(); i++)
{
String className = classes.get(i);
Class<?> clazz = null;
try
{
clazz = Class.forName(className);
Field idField = clazz.getDeclaredField("ID");
idField.setAccessible(true);
classIds.add((String) idField.get(null));
}
catch (ClassNotFoundException e1)
{
}
catch (NoSuchFieldException e)
{
}
catch (SecurityException e)
{
}
catch (IllegalArgumentException e)
{
}
catch (IllegalAccessException e)
{
}
}
}
}
You cannot create File objects from arbitrary URLs and use the usual filesystem traversal methods. Now, I'm not sure if launch4j does make any difference, but as for iterating over the contents of plain JAR file, you can use the official API:
JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile file = connection.getJarFile();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
if (e.getName().startsWith("com")) {
// ...
}
}
Above snippet lists all the entries in the JAR file referenced by url, i.e. files and directories.