I am making a game in JAVA where I want to come up with a list of files in a certain directory in my jar so I can make sure to have a list of those classes to be used in the game.
For example say in my jar I have a directory
mtd/entity/creep/
I want to get a list of all the .class files in that directory using java code from another class in the jar.
What is the best code to do so?
Old java1.4 code, but that would give you the idea:
private static List getClassesFromJARFile(String jar, String packageName) throws Error
{
final List classes = new ArrayList();
JarInputStream jarFile = null;
try
{
jarFile = new JarInputStream(new FileInputStream(jar));
JarEntry jarEntry;
do
{
try
{
jarEntry = jarFile.getNextJarEntry();
}
catch(IOException ioe)
{
throw new CCException.Error("Unable to get next jar entry from jar file '"+jar+"'", ioe);
}
if (jarEntry != null)
{
extractClassFromJar(jar, packageName, classes, jarEntry);
}
} while (jarEntry != null);
closeJarFile(jarFile);
}
catch(IOException ioe)
{
throw new CCException.Error("Unable to get Jar input stream from '"+jar+"'", ioe);
}
finally
{
closeJarFile(jarFile);
}
return classes;
}
private static void extractClassFromJar(final String jar, final String packageName, final List classes, JarEntry jarEntry) throws Error
{
String className = jarEntry.getName();
if (className.endsWith(".class"))
{
className = className.substring(0, className.length() - ".class".length());
if (className.startsWith(packageName))
{
try
{
classes.add(Class.forName(className.replace('/', '.')));
} catch (ClassNotFoundException cnfe)
{
throw new CCException.Error("unable to find class named " + className.replace('/', '.') + "' within jar '" + jar + "'", cnfe);
}
}
}
}
private static void closeJarFile(final JarInputStream jarFile)
{
if(jarFile != null)
{
try
{
jarFile.close();
}
catch(IOException ioe)
{
mockAction();
}
}
}
Probably the best approach is to list the classes at compile time.
There is a fragile runtime approach. Take you Class
(MyClass.class
of this.getClass()
). Call getProtectionDomain
. Call getCodeSource
. Call getLocation
. Call openConnection
. (Alternatively open a resource.) Cast to JarURLConnection
. Call getJarFile
. Call entries
. Iterate through checking getName
. I really do not recommend this approach.
Remember that JAR files are just ZIP files renamed, and it's very easy to read the contents of ZIP files in Java:
File jarName = null;
try
{
jarName = new File (Dir.class.getProtectionDomain().getCodeSource().getLocation().toURI());
}
catch (Exception e)
{
e.printStackTrace();
}
try
{
ZipFile zf=new ZipFile(jarName.getAbsoluteFile());
Enumeration e=zf.entries();
while (e.hasMoreElements())
{
ZipEntry ze=(ZipEntry)e.nextElement();
System.out.println(ze.getName());
}
zf.close();
} catch (IOException e)
{
e.printStackTrace();
}
It's not possible, as Java doesn't provide direct access to the jar file the classes are loaded from. You could try to parse the java.class.path system property to find it, but that wouldn't work under all circumstances. Or you could restrict on where the jar file has to reside, or provide the list of the classes in a different way (for example via the manifest file).