I have spent hours and hours searching for the answer and I just can't figure it out, I am trying to copy my resources folder which contains all the images and data files for my game I am working on out of the running jar and into
E:/Program Files/mtd/ It works fine when I run it out of eclipse, but when I export the jar and try it, I get NoSuchFileException
`JAR
Installing...
file:///C:/Users/Cam/Desktop/mtd.jar/resources to file:///E:/Program%20Files/mtd
/resources
java.nio.file.NoSuchFileException: C:\Users\Cam\Desktop\mtd.jar\resources
at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
at sun.nio.fs.WindowsFileSystemProvider.readAttributes(Unknown Source)
at java.nio.file.Files.readAttributes(Unknown Source)
at java.nio.file.FileTreeWalker.walk(Unknown Source)
at java.nio.file.FileTreeWalker.walk(Unknown Source)
at java.nio.file.Files.walkFileTree(Unknown Source)
at java.nio.file.Files.walkFileTree(Unknown Source)
at me.Zacx.mtd.main.Game.<init>(Game.java:94)
at me.Zacx.mtd.main.Game.main(Game.java:301)`
This is the code I am using:
if (!pfFolder.exists()) {
pfFolder.mkdir();
try {
URL url = getClass().getResource("/resources/");
URI uri = null;
if (url.getProtocol().equals("jar")) {
System.out.println("JAR");
JarURLConnection connect = (JarURLConnection) url.openConnection();
uri = new URI(connect.getJarFileURL().toURI().toString() + "/resources/");
} else if (url.getProtocol().equals("file")) {
System.out.println("FILE");
uri = url.toURI();
}
final Path src = Paths.get(uri);
final Path tar = Paths.get(System.getenv("ProgramFiles") + "/mtd/resources/");
System.out.println("Installing...");
System.out.println(src.toUri() + " to " + tar.toUri());
Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException {
return copy(file);
}
public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) throws IOException {
return copy(dir);
}
private FileVisitResult copy( Path fileOrDir ) throws IOException {
System.out.println("Copying " + fileOrDir.toUri() + " to " + tar.resolve( src.relativize( fileOrDir ) ).toUri());
Files.copy( fileOrDir, tar.resolve( src.relativize( fileOrDir ) ) );
return FileVisitResult.CONTINUE;
}
});
System.out.println("Done!");
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
This was harder that I thought, but here is how to do it.
here is my copy method Reference https://examples.javacodegeeks.com/core-java/io/file/4-ways-to-copy-file-in-java/
public void copyFile(String inputPath, String outputPath ) throws IOException
{
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = getClass().getResourceAsStream(inputPath);
outputStream = new FileOutputStream(outputPath);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
}
finally {
inputStream.close();
outputStream.close();
}
Please note the structure of the project of the Jar file in this image Project structure
Now I need to read the Jar file. This is a varition on this solution How can I get a resource "Folder" from inside my jar File? . Both of these methods work together to product the result. I have tested this and it works.
public class Main {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
final String pathPartOne = "test/com";
final String pathPartTwo = "/MyResources";
String pathName = "C:\\Users\\Jonathan\\Desktop\\test.jar";
JarTest test = new JarTest();
final File jarFile = new File(pathName);
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(pathPartOne+pathPartTwo + "/")) { //filter according to the path
if(name.contains("."))//has extension
{
String relavtivePath = name.substring(pathPartOne.length()+1);
String fileName = name.substring(name.lastIndexOf('/')+1);
System.out.println(relavtivePath);
System.out.println(fileName);
test.copyFile(relavtivePath, "C:\\Users\\Jonathan\\Desktop\\" + fileName);
}
}
}
jar.close();
}
}
}
Hope that helps.
The problem here is different File Systems. C:/Users/Cam/Desktop/mtd.jar is a File in the WindowsFileSystem. Since it is a file, and not a directory, you cannot access a subdirectory inside the file; C:/Users/Cam/Desktop/mtd.jar/resources is only a valid Path if mtd.jar is actually a directory instead of a file.
In order to access something on a different file system, you must use the path from the root of that file system. For example, if you have a file in D:\dir1\dir2\file, you cannot reach it using a path that begins with C:\ (symbolic links not withstanding); you must use a path that starts at the root of that file system D:\.
A jar file is just a file. It can be located anywhere within a file system, and can be moved, copied or deleted like any regular file. However, it contains within itself its own file system. There is no windows path that can be used to reference any file inside the jar's file system, just like no path starting at C:\ can reference any file within the D:\ file system.
In order to access the contents of a jar, you must open the jar as a ZipFileSystem.
// Autoclose the file system at end of try { ... } block.
try(FileSystem zip_fs = FileSystems.newFileSystem(pathToZipFile, null)) {
}
Once you have zip_fs, you can use zip_fs.getPath("/path/in/zip"); to get a Path to a file within it. This Path object will actually be a ZipFileSystemProvider path object, not a WindowsFileSystemProvider path object, but otherwise it is a Path object that can be opened, read from, etc., at least until the ZipFileSystem is closed. The biggest differences are that path.getFileSystem() will return the ZipFileSystem, and that resolve() and relativize() cannot use path objects where getFileSystem() returns different file systems.
When your project ran from Eclipse, all the resources were in the WindowsFileSystem, so walking the file system tree and copying the resources was straight forward. When your project ran from a jar, the resources were not in the default file system.
Here is a Java class that will copy resources to an installation directory. It will work in Eclipse (with all the resources as individual files), as well as when the application is packaged into a jar.
public class Installer extends SimpleFileVisitor<Path> {
public static void installResources(Path dst, Class<?> cls, String root) throws URISyntaxException, IOException {
URL location = cls.getProtectionDomain().getCodeSource().getLocation();
if (location.getProtocol().equals("file")) {
Path path = Paths.get(location.toURI());
if (location.getPath().endsWith(".jar")) {
try (FileSystem fs = FileSystems.newFileSystem(path, null)) {
installResources(dst, fs.getPath("/" + root));
}
} else {
installResources(dst, path.resolve(root));
}
} else {
throw new IllegalArgumentException("Not supported: " + location);
}
}
private static void installResources(Path dst, Path src) throws IOException {
Files.walkFileTree(src, new Installer(dst, src));
}
private final Path target, source;
private Installer(Path dst, Path src) {
target = dst;
source = src;
}
private Path resolve(Path path) {
return target.resolve(source.relativize(path).toString());
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path dst = resolve(dir);
Files.createDirectories(dst);
return super.preVisitDirectory(dir, attrs);
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path dst = resolve(file);
Files.copy(Files.newInputStream(file), dst, StandardCopyOption.REPLACE_EXISTING);
return super.visitFile(file, attrs);
}
}
Called as:
Path dst = Paths.get("C:\\Program Files\\mtd");
Installer.installResources(dst, Game.class, "resources");
I FINALLY FOUND THE ANSWER
I don't want to type out a big, long explanation but for anyone looking for the solution, here it is
`
//on startup
installDir("");
for (int i = 0; i < toInstall.size(); i++) {
File f = toInstall.get(i);
String deepPath = f.getPath().replace(f.getPath().substring(0, f.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
System.out.println(deepPath);
System.out.println("INSTALLING: " + deepPath);
installDir(deepPath);
System.out.println("INDEX: " + i);
}
public void installDir(String path) {
System.out.println(path);
final URL url = getClass().getResource("/resources/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
System.out.println("copying..." + app.getPath() + " to " + pfFolder.getPath());
String deepPath = app.getPath().replace(app.getPath().substring(0, app.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
System.out.println(deepPath);
try {
File f = new File(resources.getPath() + "/" + deepPath);
if (getExtention(app) != null) {
FileOutputStream resourceOS = new FileOutputStream(f);
byte[] byteArray = new byte[1024];
int i;
InputStream classIS = getClass().getClassLoader().getResourceAsStream("resources/" + deepPath);
//While the input stream has bytes
while ((i = classIS.read(byteArray)) > 0)
{
//Write the bytes to the output stream
resourceOS.write(byteArray, 0, i);
}
//Close streams to prevent errors
classIS.close();
resourceOS.close();
} else {
System.out.println("new dir: " + f.getPath() + " (" + toInstall.size() + ")");
f.mkdir();
toInstall.add(f);
System.out.println(toInstall.size());
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (URISyntaxException ex) {
// never happens
}
}
}`
I am writing Java application and I want to write some simple plugin system. I want to have base class Plugin. Other classes extends Plugin, these files are in some other directory out of class path.
public class Plugin {
public Plugin() {
//code
}
public void proc() {
//code
}
}
and class loader:
public class PluginLoader {
private static final FilenameFilter filter = new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return Pattern.matches("^.*[a-zA-Z]*[.]class$", name);
}
};
public static final String removeExtension(String str) {
if (str == null)
return null;
int pos = str.lastIndexOf(".");
if (pos == -1)
return str;
return str.substring(0, pos);
}
#SuppressWarnings("unchecked")
public static LinkedList<Plugin> loadEffects(String path) {
LinkedList<Plugin> result = new LinkedList<Plugin>();
Plugin instance = null;
File[] classesList = null;
System.out.println("Searching in " + path);
try {
File classDir = new File(path);
URL[] url = { classDir.toURI().toURL() };
URLClassLoader urlLoader = new URLClassLoader(url);
String filename;
classesList = classDir.listFiles(filter);
System.out.println(classesList.length + " class files found:");
for (File file : classesList) {
System.out.println("- " + file.getName());
}
for (File file : classesList) {
filename = removeExtension(file.getName());
if (filename.equals(".") || filename.equals("..") || filename.startsWith("."))
continue;
if (filename.equals("Plugin")) {
System.err.println("File name is Plugin");
continue;
}
System.out.println("Reading " + filename);
instance = (Plugin) urlLoader.findClass(filename).getConstructor().newInstance();
System.out.println("Adding: " + url + ", " + filename);
result.push(instance);
}
urlLoader.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
This code causes java.lang.NoClassDefFoundError: D:\test\PluginImpl/class (wrong name: test/PluginImpl). Plugin class is in D:\test.
You are calling File#getAbsolutePath() which will include the entire path and the drive letter (D:\). The class name stored in the class does not match with the class name you provided so Java throws an error.
Try calling findClass with just the class name and it should work
I'm trying to load classes from tree structure similar to JavaEE web application. So I have directory which contains compiled classes (/classes) and sibling directory with libraries (/lib). In classes package structure I have compiled class com.test.Test.class with simple logic:
package com.test;
import org.json.JSONException;
import org.json.JSONObject;
import com.test.GameObjectState;
public class Test extends Thread {
#Override
public void run() {
JSONObject json = new JSONObject();
try {
json.put("state", GameObjectState.REMOVED);
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println(json.toString());
}
}
And here's my main:
ClassLoader classLoader = ClassLoaderFactory.createClassLoader(repository);
Class<?> testClass = classLoader.loadClass("com.test.Test");
Thread t = (Thread) testClass.newInstance();
t.start();
My ClassLoader implementation follows the delegation model. During context init finally JSONObject & JSONException will be scanned and found in lib/java-json.jar (matching corresponding JarEntry binary name inside). When JarEntry found - creates InputStream to load data in buffer to define class.
JSONException loaded successfully while JSONObject failed on ClassFormatError. Magic is that I've extracted JSONObject in /classes directory manually from java-json.jar and no ClassFormatError has been thrown, JSONObject created and output printed successfully. Probably should note that JSONObject has inner/nested classes compiled in library.
And here's my classloader
public class WebAppClassLoader extends URLClassLoader {
private static final String CLASS_FILE_SUFFIX = ".class";
private static final String JAR_FILE_SUFFIX = ".jar";
private static final String CLASS_NOT_FOUND = "Class %s not found";
private File classesDirectory;
private Map<String, Class<?>> classes;
public WebAppClassLoader(File classesDirectory, URL[] urls) {
super(urls);
this.classesDirectory = classesDirectory;
this.classes = new ConcurrentHashMap<String, Class<?>>();
}
public static WebAppClassLoader newInstance(File classesDirectory, File libDirectory) throws IOException {
File[] jars = libDirectory.listFiles(new JarFilter());
Set<URL> jarUrls = new HashSet<URL>();
for(File jar : jars) {
jarUrls.add(jar.toURI().toURL());
}
return new WebAppClassLoader(classesDirectory, jarUrls.toArray(new URL[jarUrls.size()]));
}
protected static class DirectoryFilter implements FileFilter {
#Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
}
protected static class ClassFilter implements FileFilter {
#Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(CLASS_FILE_SUFFIX);
}
}
protected static class JarFilter implements FileFilter {
#Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(JAR_FILE_SUFFIX);
}
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> result = null;
byte[] buffer = null;
try {
buffer = scanClasses(classesDirectory, asPathName(name));
if(buffer == null) {
buffer = scanLib(asPathName(name).replace('\\', '/'));
}
if(buffer != null) {
result = defineClass(name, buffer, 0, buffer.length);
classes.put(name, result);
}
} catch(IOException e) {
e.printStackTrace();
}
if(result == null) {
String error = String.format(CLASS_NOT_FOUND, name);
throw new ClassNotFoundException(error);
}
return result;
}
private byte[] scanClasses(File file, String path) throws IOException {
byte[] result = null;
File[] directories = file.listFiles(new DirectoryFilter());
File[] classes = file.listFiles(new ClassFilter());
for(File classFile : classes) {
if(classFile.getAbsolutePath().endsWith(path)) {
InputStream input = new FileInputStream(classFile);
input.read(result = new byte[input.available()]);
input.close();
break;
}
}
if(result == null) {
for(File directory : directories) {
result = scanClasses(directory, path);
if(result != null) {
break;
}
}
}
return result;
}
private byte[] scanLib(String path) throws IOException {
byte[] result = null;
for(URL url : getURLs()) urlLoop:{
File file = new File(url.getFile());
JarFile jarFile = new JarFile(file);
try {
Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(entry.getName().endsWith(path)) {
InputStream input = jarFile.getInputStream(entry);
input.read(result = new byte[input.available()]);
break urlLoop;
}
}
}finally {
jarFile.close();
}
}
return result;
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> result = null;
if(classes.containsKey(name)) {
result = classes.get(name);
}else{
result = super.loadClass(name);
}
return result;
}
private static String asPathName(String binaryName) {
return binaryName.replace('.', '\\').concat(CLASS_FILE_SUFFIX);
}
}
Thanks
This question already has answers here:
Can you find all classes in a package using reflection?
(30 answers)
Closed 9 years ago.
So I have a package that has classes that extend JPanel and I want to add them as tabs dynamically. At the beginning I used a factory and I registered all the classes in it and it worked, but now I want load all the classes in the package without knowing their names. I've tried several things including Reflections library (which I found very confusing) and I couldn't get them to work. I appreciate any help.
Here's one of my trials:
public static void registerTab() {
String pkg = TabA.class.getPackage().getName();
String relPath = pkg.replace('.', '/');
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
if (resource == null) {
throw new RuntimeException("Unexpected problem: No resource for "
+ relPath);
}
File f = new File(resource.getPath());
String[] files = f.list();
for (int i = 0; i < files.length; i++) {
String fileName = files[i];
String className = null;
String fileNm = null;
if (fileName.endsWith(".class")) {
fileNm = fileName.substring(0, fileName.length() - 6);
className = pkg + '.' + fileNm;
}
if (className != null) {
if (!tabClasses.containsKey(className))
tabClasses.put(fileNm, className);
}
}
}
Here is a custom solution I developed to find all the classes of a package:
public class ClassFinder {
private static final char PKG_SEPARATOR = '.';
private static final char DIR_SEPARATOR = '/';
private static final String CLASS_FILE_SUFFIX = ".class";
private static final String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the package '%s' exists?";
public static List<Class<?>> find(String scannedPackage) {
String scannedPath = scannedPackage.replace(PKG_SEPARATOR, DIR_SEPARATOR);
URL scannedUrl = Thread.currentThread().getContextClassLoader().getResource(scannedPath);
if (scannedUrl == null) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage));
}
File scannedDir = new File(scannedUrl.getFile());
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File file : scannedDir.listFiles()) {
classes.addAll(find(file, scannedPackage));
}
return classes;
}
private static List<Class<?>> find(File file, String scannedPackage) {
List<Class<?>> classes = new ArrayList<Class<?>>();
String resource = scannedPackage + PKG_SEPARATOR + file.getName();
if (file.isDirectory()) {
for (File child : file.listFiles()) {
classes.addAll(find(child, resource));
}
} else if (resource.endsWith(CLASS_FILE_SUFFIX)) {
int endIndex = resource.length() - CLASS_FILE_SUFFIX.length();
String className = resource.substring(0, endIndex);
try {
classes.add(Class.forName(className));
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
Then, just use:
List<Class<?>> classes = ClassFinder.find("com.package");
This is the common approach with working with loading class dynamically:
try {
File file = new File(JAR_FILE);
String classToLoad = "com.mycompany.MyClass";
URL jarUrl = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");
URLClassLoader loader = new URLClassLoader(new URL[] {jarUrl}, Thread.currentThread().getContextClassLoader());
Class c = loader.loadClass(classToLoad);
} catch (Exception e) {
e.printStackTrace();
}
However, I need an approach where:
We don't need to create a File (as the jar I am trying to process is a byte array[] when fetched)
Or we won't need to create a temporary file from byte[] array (as AppEngine, the platform I work with does not allow to create temporary files)
You will have to create your own class loader.
Something like this, basic idea in pseudo code:
class MyClassLoader extends ClassLoader {
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
JarInputStream jis = new JarInputStream(new ByteArrayInputStream(bytearrayJarData));
JarEntry entry = jis.getNextJarEntry();
while (entry != null) {
//compare entry to requested class
// if match, return Byte data
// else entry = jis.getNextJarEntry();
}
return null; // nothing found
}
}
Write your own ClassLoader and override findClass(). There you can use defineClass() to load your byte[].