Inside my resources folder I have a folder called init. I want to copy that folder and everything inside of it to the outside of the jar in a folder called ready. And I want to do that without using any external libraries, just pure java.
I have tried the following
public static void copyFromJar(String source, final Path target)
throws
URISyntaxException,
IOException
{
URI resource = ServerInitializer.class.getResource("").toURI();
FileSystem fileSystem = FileSystems.newFileSystem(resource, Collections.<String, String>emptyMap());
final Path jarPath = fileSystem.getPath(source);
Files.walkFileTree(jarPath, new SimpleFileVisitor<>()
{
private Path currentTarget;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws
IOException
{
currentTarget = target.resolve(jarPath.relativize(dir).toString());
Files.createDirectories(currentTarget);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws
IOException
{
Files.copy(file, target.resolve(jarPath.relativize(file).toString()),
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}
However my application already dies at line
FileSystem fileSystem = FileSystems.newFileSystem(resource, Collections.<String, String>emptyMap());
with exception
java.lang.IllegalArgumentException: Path component should be '/'
when I call
copyFromJar("/init", Paths.get("ready");
Any idea what I am doing wrong? Or can someone provide me code to copy directory from jar to outside of it without using any external libraries?
Just for reference I already looked at this solution but it is too old and uses apache library but I need pure java solution that works both on windows and linux.
That's a crazy complicated way to do it. It's also dependent entirely on your app being in a jar, which makes testing, deployments into runtime-modularized loaders, etc - tricky.
There are much easier ways to do this. Specifically,
SomeClass.class.getResourceAsStream("/foo/bar")will get you anInputStreamfor the entry/foo/barin the same classpath root as where the class file representingSomeClasslives - be it a jar file, a plain old directory, or something more exotic.That's how you should 'copy' your files over. This trivial code:
Now all you need is a list of all files to be copied. The classpath abstraction simply does not support listing. So, any attempt to hack that in there is just that: A hack. It'll fail when you e.g. have modularized loaders and the like. You can just do that - you're already writing code that asplodes on you if your code is not in a jar file. It's not hard to write a method that gives you a list of all contents for both 'dir on the file system' based classpaths as well as 'jar file' based ones, but there is a ready alternative: Make a text file with a known name that lists all resources at compile time. You can write it yourself, or you can script it. Can be as trivial as
ls src/resources/* > filesToCopy.txtor whatnot. You can also use annotation processors to produce such a file.Once you know the file exists (it's in the jar same as the files you want to copy), read it with
getResourceAsStreamjust the same way, and now you have a list of resources to write out using the above code. That trick means your code is entirely compatible with the API of ClassLoader: You are just relying on the guaranteed functionality of 'get me an inputstream with the full data of this named resource' and nothing else.