List classes in package
Is there a JSR for listing packages within a class?
For example, instead of writing an algorithm to disect "java.class.path" list all the files and stick them in a map, the ClassLoader could do this as classes are created. Such a document would also suggest functions simmilar to String [] getClassnames() and Class [] getClasses() in java.lang.Package.
To me this sticks out as a sore thumb/missing feature in Java's reflection capabilities.
[462 byte] By [
kpaulsa] at [2007-9-28 6:32:15]

I agree completely with you, it's missing !Package class should implement a getClasses() method.will it come soon in the next releases ?regards,erwann.
http://www.javaworld.com/javaworld/javatips/jw-javatip113.htmlThis is an example of treating the packages as directories and listing files in them (class files).
There might be some limited value in a getLoadedClasses instance method on ClassLoader
It's not possible to write a getClasses method on Package because this information simply isn't available. If I load my classes over a socket, how is the client supposed to divine all of the class names on the (not necessarily Java based) server ?
Plus I just don't see a lot of value to the method. What are you trying to achieve that would make this useful ?
Dave.
The use I see for it is that I want to tell my program to automatically discover the classes in a Package so that it can auto-load new implementation classes that I write and drop into a specified directory. The way I do it now is to register each class in my configuration XML file manually. This is an unnecessary step that I'd like to skip. I'd like the program to discover the details of this automatically. This used to be a common practice with DLL writing when I was a Delphi programmer and is still done in many environmens.
In my shipment pricing tool, I want to add carriers, services, fees, and other extensions to the application automatically. I could even do a hot deploy and have the application late-load any new implementation classes every 30 minutes or something.
I found the JavaWorld code above almost worked...there were a couple bugs and the API wasn't quite what I wanted...so here is a refactored version. If you find it useful, enjoy (if you find it *really* useful, PayPal me a buck ;-)
- Jon Peck
peck_jon@yahoo.com
/**
* list Classes inside a given package
* @author Jon Peck http://jonpeck.com (adapted from http://www.javaworld.com/javaworld/javatips/jw-javatip113.html)
* @param pckgname String name of a Package, EG "java.lang"
* @return Class[] classes inside the root of the given package
* @throws ClassNotFoundException if the Package is invalid
*/
public static Class[] getClasses(String pckgname) throws ClassNotFoundException {
ArrayList classes=new ArrayList();
// Get a File object for the package
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.endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(pckgname+'.'+files.substring(0, files.length()-6)));
}
}
} else {
throw new ClassNotFoundException(pckgname+" does not appear to be a valid package");
}
Class[] classesA=new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}>
quick note on that last post...due either to my miscopying or a bug in the forum software, the index following "files" was deleted in a few places...so them it back in when running the code!
Insert [i]
after "files" on the following:
files.endsWith
files.substring
files.length
Or just go here for the code: http://www.jonpeck.com/forum/viewtopic.php?t=88
Here is a better version. Java 5 and better error messages.
public static Class[] getClasses(String pckgname)
throws ClassNotFoundException {
ArrayList<Class> classes = new ArrayList<Class>();
// Get a File object for the package
File directory = null;
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
String path = '/' + pckgname.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
directory = new File(resource.getFile());
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " (" + directory
+ ") 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
classes.add(Class.forName(pckgname + '.'
+ files[i].substring(0, files[i].length() - 6)));
}
}
} else {
throw new ClassNotFoundException(pckgname
+ " does not appear to be a valid package");
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}
The code seems correct but does not work in Java 5.I get a No resource for ... on my package.Is this a change in Java 5 where introspection of classes as resources maybe doestnot work?
I got the above code to work by changing the line:"String path = '/' + pckgname.replace('.', '/');"to "String path = pckgname.replace('.', '/');"-Nick
I have a package called shape2 that is abstract and is the base class for my other shape classes, e.g circle, hexagon, rectangle etc. They all inherit from Shape.java which is declared as a package.
I need to be able to list the contents of the package, i.e which classes inherit from shape.java
This is how I am calling it
public static void main(String[] args) {
try {
Class[] test = getClasses("shape2");
System.out.println(test);
} catch (Exception ex) {
System.out.println(ex);
}
}
The error I get is caught in ELSE statement at the end. Are the paths relative to the directory? I am using NetBeans IDE 4.1 and my .java files and .class file are in the default locations. Is it to do with the / instead of \, How can i verify what path the getClasses is looking in. What am I missing
I had a similar problem with using packages ang classes this way. "MartinHilpert" sended me this adress: http://java.sun.com/docs/books/tutorial/reflect/index.htmlIt is a preatty useful tutorial for the Reflection API
That's great but it doesn't seem to work if the package is in a jar file.The resource URL looks greate but calling list() on the File returns null?Any ideas?ThanksMike
mepha at 2007-7-9 17:44:14 >

> That's great but it doesn't seem to work if the
> package is in a jar file.
> The resource URL looks greate but calling list() on
> the File returns null?
> Any ideas?
>
> Thanks
> Mike
I am having a problem in using this particular code
Thread.currentThread().getContextClassLoader().getSystemResource("/"+pckgname.replace('.', '/')
returns me null.
What could be wrong?
I have found the following mods overcame my problems with the example given:
// String path = '/' + pckgname.replace('.', '/');
String path = pckgname.replace('.', '/');
which was given by someone else above but also:
// directory = new File(resource.getFile());
directory = new File(URLDecoder.decode(resource.getPath(),"UTF-8"));
The URL decoding was necessary to convert encoded spaces in the URL pathname (appear as %20) back into unicode as used by java.io.File
The code given in previous posts only searches the first resource found for a given package name. In reality classes in a package may be split up across multple jars/directories (src and test, for example). I've modified the code (below) to look in all resources for a package path.
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* @param pckgname
*the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
public static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
// This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
ArrayList<File> directories = new ArrayList<File>();
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
String path = pckgname.replace('.', '/');
// Ask for all resources for the path
Enumeration<URL> resources = cld.getResources(path);
while (resources.hasMoreElements()) {
directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
}
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
} catch (UnsupportedEncodingException encex) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
} catch (IOException ioex) {
throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
}
ArrayList<Class> classes = new ArrayList<Class>();
// For every directory identified capture all the .class files
for (File directory : directories) {
if (directory.exists()) {
// Get the list of the files contained in the package
String[] files = directory.list();
for (String file : files) {
// we are only interested in .class files
if (file.endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(pckgname + '.' + file.substring(0, file.length() - 6)));
}
}
} else {
throw new ClassNotFoundException(pckgname + " (" + directory.getPath() + ") does not appear to be a valid package");
}
}
return classes;
}
I stumbled upon this discussion because i needed the exact same thing: a way of walking over all the classes in my classpath, identifying the ones that extend (directly or not) a specific class and then adding them to a map. That would save me some hardcoding which i really hate to do :)
So, everything went well for me for the filesystem. No big problem there. But I had absolutely no clue of how to work with classes inside jar files. I tried plain zip files, but it just didn't work. Anyway, Daniel Le Berre's article in JavaWorld was helpful, but only with what regards the JarURLConnection and the jar entries. His example didn't work at all for jars. And that is because in jars the resource that should be looked for is the class itself, not the package with .->/ And that is how the connection should be opened.
URL urlJar = this.getClass( ).getResource( tosubclassname );
urlJar = new URL( "jar:"
+ urlJar.getPath( ).substring( 0,
urlJar.getPath( )
.indexOf( "!" )
+ 2 ) );
JarURLConnection conn = (JarURLConnection)urlJar.openConnection( );
What you see in the middle is due to the fact that the connection has to be opened to the start of the jar.
If your path would be jar:file:/D:/jars/tests.zip!/com.tests.Action.class then the connection has to be opened with the path cut until !/: jar:file:/D:/jars/tests.zip!/
Hello, it only lists classes from package within the project.How do i list classes from diferent packages around, like java.lang , org.eclipse.uml2, etc etc :DLove all of you.Thanks
> I am having a problem in using this particular code
>
> Thread.currentThread().getContextClassLoader().getS
> ystemResource("/"+pckgname.replace('.', '/')
>
> returns me null.
> What could be wrong?
The main problem of this code is, that getResource() or getSystemResource() are returning an URL, not a filename. An url is url-encoded (e.g. spaces are denoted as "%20") and needs to be decoded before it can be used as file-/directoryname.
Without URL-decoding the function fails if a space, an umlaut or any other special character is part of the path to the package to be enumerated...
The following code works for me:
URL packageURL = Thread.currentThread().getContextClassLoader().getResource(
pckgname.replace('.', '/'));
directory = new File(URLDecoder.decode(packageURL.getFile()));
I know that the used URLDecoder.decode() method is marked depreciated because it uses the "platform's default encoding".
IMHO in this case the "platform's default encoding" is the only correct encoding. Any encoding specified in the code would be only correct on some systems.
Jan
The following method lists all classes which are placed in a package from all Jar-Files in the specified directory:
Have fun...
public static Class[] getClassesFromFileJarFile(String pckgname, String baseDirPath) throws ClassNotFoundException
{
ArrayList<Class> classes = new ArrayList<Class>();
String path = pckgname.replace('.', '/') + "/";
File mF = new File(baseDirPath);
String[] files = mF.list();
ArrayList jars = new ArrayList();
for (int i = 0; i < files.length; i++)
if (files[i].endsWith(".jar")) jars.add(files[i]);
for (int i = 0; i < jars.size(); i++)
{
try
{
JarFile currentFile = new JarFile(jars.get(i).toString());
for (Enumeration e = currentFile.entries(); e.hasMoreElements(); )
{
JarEntry current = (JarEntry) e.nextElement();
if(current.getName().length() > path.length() && current.getName().substring(0, path.length()).equals(path) && current.getName().endsWith(".class"))
classes.add(Class.forName(current.getName().replaceAll("/", ".").replace(".class", "")));
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}