Files.readString(Paths.get(ClassPathResource("temp.md").uri)
The markdown text in the src > main > resources folder is loaded through the code above. In the local environment, the file location is checked and data is loaded normally, but when the built jar is executed on ec2, the following error is returned.
I think, the path to the built jar is wrong, but I don't know how to solve it, please advise
File loading from files located in the classpath work differently when the application is packed as a JAR.
Check this Baeldung article for the detailed explanation.
This will work independent from the way the code is packaged:
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.util.ResourceUtils;
import java.nio.file.Files;
#UtilityClass
public class FileTestUtils {
#SneakyThrows
public byte[] getFileAsBytes(String fileName) {
return Files.readAllBytes(ResourceUtils.getFile("classpath:" + fileName).toPath());
}
#SneakyThrows
public String getFileAsString(String fileName) {
return new String(Files.readAllBytes(ResourceUtils.getFile("classpath:" + fileName).toPath()));
}
}
Alternatively we can read resource using classloader instance.
ClassLoader classLoader = SpringBootResourcesApplication.class.getClassLoader();
File file = new File(classLoader.getResource("temp.md").getFile());
Related
I'm trying to write a JSON file with GSON in the resources folder without using src/main/resources:
package repository;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.tinylog.Logger;
import java.io.*;
import java.util.List;
public class GsonRepository<T> extends Repository<T> {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public GsonRepository(Class<T> elementType) {
super(elementType);
}
public void saveToFile(String resourceName) throws IOException {
try (var writer = new FileWriter(this.getClass().getResource(resourceName).getPath())) {
GSON.toJson(elements, writer);
}
}
}
This doesn't seem to be working. What am I doing wrong?
I'm trying to write a JSON file with GSON in the resources folder
Well, that's your problem. Because that's completely impossible.
The 'resources' folder is a thing that exists on the computer of the developer only. Hence, you can't write to a thing that doesn't exist.
The resources folder is solely for read-only resources. Think files of tabular data (say, a list of country names and phone number prefixes), icon files, HTML templates, that sort of business.
You can only load these files with GsonRepository.class.getResource and (.getResourceAsStream) - any attempt to treat them as files will work during dev and then fail when you deploy.
If you have config files or save files, these don't go in the resources folder, aren't loaded with .getResource at all. You should place these in the user's home dir (System.getProperty("user.home")): The directory where the java app is installed will not be writable by the app itself (or if it is, you have a pretty badly configured OS for security purposes. Windows is, of course, badly configured. That doesn't make it a good idea to start sticking user-editable data files in the install dir of the app, though!)
new FileWriter(this.getClass().getResource
this doesn't make sense. In java, File means file. And a resource is not a file - for example, an entry in a jar file is not a file. But, resources tend to be entries in jar files.
Note that it's YourClass.class.getResource, not .getClass().getResource - the latter can break when you subclass, the former never breaks and is therefore superior in every way. When there are 2 ways to do a thing that are virtually identical in readability, and one is applicable to strictly more scenarios than the other, then never use the other.
I am working on creating a personal utility downloader for things like Malwarebytes, Adware Cleaner, and etc. But I have never worked with anything like this before. I searched around and found some documentation on how to download files from a URL into a directory, but I haven't been able to get it to work yet. The first time it turned the directory into a file that was unsuable and now that I have changed the URL it is failing to download due to the errors listed at the bottom. Could someone point me in the right direction or tell me what I am doing wrong?
package com.kcc;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class Testing2 {
public static String testURL;
public static String saveDir;
public static void main(String[] args) throws IOException {
testURL ="https://download.bleepingcomputer.com/dl/a652734ff3304da2530acb93754c1bf7/5af5a320/windows/security/security-utilities/a/adwcleaner/AdwCleaner.exe";
//"https://download.toolslib.net/download/file/1/1511?s=2LPvu8kniU2T794QD0FXSN21jxnJOqLP";
saveDir = "C:\\Users\\Austin\\Desktop\\kccutil";
download(testURL, saveDir);
}
private static Path download(String sourceURL, String targetDirectory) throws IOException
{
URL url = new URL(sourceURL);
String fileName = sourceURL.substring(sourceURL.lastIndexOf('/') + 1, sourceURL.length());
Path targetPath = new File(targetDirectory + File.separator + fileName).toPath();
Files.copy(url.openStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
return targetPath;
}
}
I am currently getting these errors
Exception in thread "main" java.nio.file.NoSuchFileException: C:\Users\Austin\Desktop\kccutil\AdwCleaner.exe
at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230)
at java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:434)
at java.nio.file.Files.newOutputStream(Files.java:216)
at java.nio.file.Files.copy(Files.java:3016)
at com.kcc.Testing2.download(Testing2.java:25)
at com.kcc.Testing2.main(Testing2.java:17)
EDIT: For the error above, turns out the directory wasn't created. But now I am receiving a new error
Exception in thread "main" java.io.FileNotFoundException: https://download.bleepingcomputer.com/dl/a652734ff3304da2530acb93754c1bf7/5af5a320/windows/security/security-utilities/a/adwcleaner/AdwCleaner.exe
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1872)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at java.net.URL.openStream(URL.java:1045)
at com.kcc.Testing2.download(Testing2.java:25)
at com.kcc.Testing2.main(Testing2.java:17)
For the debugging purposes you can try saving files to working directory (i.e. refer to . folder). Using this approach you can access file by it's filename.
For the future I recommend you using Java 7 NIO Api: Paths.get() - for originally building path from parts, path.parent() - to refer to parent directory, path.resolve() - to build child path.
If you want to download a file you should use FTP server and not HTTP in case you have the executable.
But if you already have an HTTP link on web who calls a downloadable .exe (Like in your case), you don't really need a download method. You just need to send an http request to navigator (preferably if it is a web app), something like:
File htmlFile = new File(url);
Desktop.getDesktop().browse(htmlFile.toURI());
Or you can download that file using Apache Common IO's FileUtils:
import org.apache.commons.io.FileUtils;
FileUtils.copyURLToFile(url, file_destination);
Or you can check this response using Java NIO
I'm writing a program that is required to save a project in a chosen directory. Every project directory must include an HTML file, which may have varying elements depending on the state of the project, along with a standard library of JavaScript files in a sub-directory.
I'm not familiar with the means by which this would usually be accomplished in Java (or even how it could be accomplished in theory). Is there a particular tool, technique or library out there that would be suited to this task? Note that I'm using Eclipse as my IDE. I've been thinking about generating the files required using templates of some kind, and/or extracting them from a package, but I'm very new to this kind of problem and am not sure how to proceed from here.
EDIT: Elaborating further
My project is a small utility for my personal use, so maintainability won't be much of an issue. I'm using Java 8. Within each user created project there will only be three unchanging .js files in the library and a small html file that will be launched in a browser to run the scripts, along with a user generated .js file. Very basic stuff.
EDIT: Problem solved... I think
I've come up with my own partial solution, and I think I can figure the rest out from here, but D.B.'s post was still informative and helpful, so I'm accepting it as the answer.
I realize that my original question wasn't specific enough. I was hoping to hide my static script resources and the template for the HTML file so that they could not be directly accessible from the file system. I had been considering placing them within some kind of package file that would reside in the same directory as the application jar. It slipped my mind, however, that I could simply place them in a resource folder within the jar itself.
Creating the libraries directory within the user specified project directory:
libraryDir = projectDir.resolve("libraries");
new File(libraryDir.toUri()).mkdirs(); // Create libraries directory
// Unpack library resources to project
unpackLibrary("file1.js");
unpackLibrary("file2.js");
unpackLibrary("file3.js");
the unpackLibrary function:
private void unpackLibrary(String scriptName) {
String resourcePath = "/libraries/" + scriptName;
Path fileName = Paths.get(resourcePath).getFileName();
try (InputStream in = getClass().getResourceAsStream(resourcePath);) {
Files.copy(in, libraryDir.resolve(fileName));
}
catch (FileAlreadyExistsException e) {
System.out.println("File unpack failed: File already exists (" + e.getFile() + ")");
}
catch (IOException e) {
e.printStackTrace();
}
}
The application will work with a project in a temporary directory until the user decides to save it, in which case I will use D.B.'s suggestions to copy the project files to the new directory.
How you proceed is entirely up to you and your project requirements. It really depends on what these files are going to be used for, how they will be maintained, etc. Using templates would seem to be a good solution since then you can simply replace variables/tokens in the template when generating a new set of project files. Whether or not those templates are somehow packaged/zipped is again up to you. If you have a good reason to package or zip them then do so, if not then there's really no reason they would need to be.
If you do use templates the basic technique would be: read the template, process the template by replacing variables/tokens with values, write the resulting data to a new file.
If you're using Java 1.7 or higher take a look at the java.nio package and the tutorial for Java file I/O featuring NIO as it's very good and will help you to understand the API and how to use it to manipulate files.
Hope this helps to get you started.
EDIT -- Here is the update I promised in my comment:
As I understand it the program must copy a set of static files into a new directory. Since you have a "project" concept going on I don't think that the code should replace existing files, so if the project files already exist this program will fail out.
Assumptions:
The static template files will reside in a folder that exists inside the current working directory for the application jar file. That is, if your executable jar lives in the folder C:\MyProgram (assuming a Windows file system) then your templates will exist in a folder within that folder - C:\MyProgram\templates for example.
The program should create the entire directory tree structure up to and including the "project" folder.
Here is the code:
SaveProjectFolderMain.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class SaveProjectFolderMain {
//Note: In eclipse your templates folder must be inside the eclipse project's root directory
// in order for this path to be correct. E.g. myProject/templates
// If this program is run outside of eclipse it will look for a templates folder
// in the current working directory - the directory in which the jar resides.
// E.g. if you jar lives in C:\myProgram then your templates should live in
// C:\myProgram\templates (obviously this is a Windows file system example)
private static final Path TEMPLATES_DIR = Paths.get("templates");
public static void main(String[] args) {
//Copies the template files to a new folder called "testProject"
// creating the project folder if it does not exist.
saveProjectDir(Paths.get("testProject"));
}
public static void saveProjectDir(Path dirToSaveTo){
try {
if(!Files.exists(dirToSaveTo)){
System.out.println("The directory " + dirToSaveTo.toAbsolutePath() + " does not exist, it is being created.");
Files.createDirectories(dirToSaveTo);
}
Files.walkFileTree(TEMPLATES_DIR, new CopyFileVisitor(dirToSaveTo));
} catch (IOException e) {
System.err.println("Unable to copy template files to the save location: " + dirToSaveTo.toAbsolutePath());
e.printStackTrace();
}
}
}
CopyFileVisitor.java
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class CopyFileVisitor extends SimpleFileVisitor<Path> {
private final Path targetPath;
private Path sourcePath = null;
public CopyFileVisitor(Path targetPath) {
this.targetPath = targetPath;
}
#Override
public FileVisitResult preVisitDirectory(final Path dir,
final BasicFileAttributes attrs) throws IOException {
if (sourcePath == null) {
sourcePath = dir;
} else {
Path destDir = targetPath.resolve(sourcePath.relativize(dir));
System.out.println("Creating directory: " + destDir);
Files.createDirectories(destDir);
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs) throws IOException {
Path destFilePath = targetPath.resolve(sourcePath.relativize(file));
System.out.println("Copying " + file.toAbsolutePath() + " to " + destFilePath.toAbsolutePath());
Files.copy(file, destFilePath);
return FileVisitResult.CONTINUE;
}
}
My code runs inside a JAR file and I need to get the full path of that file.
For example, my JAR is called example.jar and is located at D:\example\
So I need to get "D:\example\example.jar" by some code inside that jar.
I have tried many methods to get that path, but none of them worked correctly.
One of them is
getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()
Many people say that this works for them, but it returns "rsrc:./" for me.
I have searched after that and I noticed that my MANIFEST.MF contains this:
Manifest-Version: 1.0
Rsrc-Class-Path: ./
Class-Path: .
Rsrc-Main-Class: Example
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
I don't know what that means, but if I remove that Rsrc stuff and replace the other things with it, it says the jar is broken. I think this is the reason why it doesn't work. Does anybody know what this means?
PS: I'm running my JAR using a BAT file.
I stumbled over this problem too and leave my investigations here for people who ask themselves in the future what the rsrc means.
I'm using Eclipse Mars 1 and try to export my project as a runnable JAR. There I can choose the library handling and decide between:
Extract required libraries into generated JAR
Package required libraries into generated JAR
Copy required libraries into a sub-folder next to the generated JAR
The line to be tested is
System.out.println(MyClass.class.getProtectionDomain().getCodeSource().getLocation());
the JAR file's name is MyJar.jar (which will be put on desktop), Project's name and folder is MyProject.
Results:
file:/C:/Users/admin/Desktop/MyJar.jar
rsrc:./
file:/C:/Users/admin/Desktop/MyJar.jar
<means running in Eclipse> file:/C:/Development/workspace/MyProject/target/classes/
I wrote a convinience method for that:
public class SystemUtils {
/**
* Let no one instanciate this class.
*/
private SystemUtils() {}
/**
* If the current JVM was started from within a JAR file.
* #return <code>Boolean.TRUE</code> if it is, <code>Boolean.FALSE</code> if it is not, <code>null</code> if unknown.
*/
public static Boolean executedFromWithinJar() {
Boolean withinJar = null;
try {
String location = SystemUtils.class.getProtectionDomain().getCodeSource().getLocation().toString();
if (location.startsWith("rsrc:")
|| location.endsWith(".jar") && !new File(location.substring(location.indexOf(':') + 1)).isDirectory())
withinJar = Boolean.TRUE;
else
withinJar = Boolean.FALSE;
}
catch (Exception ex) {/* value is still null */}
return withinJar;
}
}
Based on your comments, it appears your real question is how to copy files from inside your application .jar. You can do that with something like this:
String jarEntry = "/files/data.txt";
Path destination = Paths.get(
System.getProperty("user.home"), "Downloads", "data.txt");
try (InputStream stream = getClass().getResourceAsStream(jarEntry)) {
Files.copy(stream, destination);
}
Class.getResource and Class.getResourceAsStream read data from the classpath, usually as an entry in a .jar file that is on the classpath, such as your own application’s .jar file.
A file embedded in the classpath is normally called an application resource or just “resource” for short. Resources are always specified using the forward slash (/) as a directory separator, on all platforms, even Windows.
If you are not sure what string you should pass to getResource or getResourceAsStream, examine the contents of your .jar file.
package com.example;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
public class HomeJar {
public static void main(String[] args) throws IOException {
URL u = HomeJar.class.getProtectionDomain().getCodeSource().getLocation();
File f = new File(u.getPath().toString());
if (!f.isFile()) {
throw new RuntimeException("'" + u + "' isn't a jar");
}
try (JarInputStream jis = new JarInputStream(new BufferedInputStream(new FileInputStream(f)))) {
JarEntry je = jis.getNextJarEntry();
while (je != null) {
System.out.println(je.getName());
je = jis.getNextJarEntry();
}
}
}
}
I just have created a new workspace and moved every project into it and everything works fine now, I think it was a bug or something...Thank you all for your help!
This was the exact problem I had today. I finally found this:
To get the jar-file location when packaging with eclipses Package required libraries into generated JAR one can use this (insert name of the calling class in the <>:
var thisClassesResourcePath = <CLASSNAME>.class.getName().replace('.', '/') + ".class";
var resource = ClassLoader.getSystemResource(thisClassesResourcePath);
var path = resource.getPath();
var jarUrl = new URL(path.substring(0, path.lastIndexOf("jar!") + 3));
This is what I've tried:
String myPath = myStaticClass.class.getResource("en-us").getPath();
// returns C:/Users/Charles/Workspace/ProjectName/target/classes/
My resources are in C:/Users/Charles/Workspace/ProjectName/src/main/resources
Does anyone know why this is happening?
Edit:
I suppose I should have mentioned that the path is being used in a library to load resources, but is failing.
That is where your compiled code is put when you use maven to build your project. Your resources are being copied to the target/classes folder as part of the build process.
If you then deploy your application to another location, you will find that your code will return the new path to the resource.
Edit
As per your comment, try using the following to load your resource:
InputStream resourceStream = myStaticClass.class.getClassLoader().getResourceAsStream("en-us");
This uses the current class's class loader to locate and provide an InputStream to your resource.
When you run mvn compile, one of the steps along the way is to copy your resources directory to the target/classes directory. Now usually if you call myStaticClass.class.getResource, the path you pass in will have target/classes as the root. So lets say you have a file at src/main/resources/my.file.txt You will be able to get it by calling myStaticClass.class.getResource("/my.file.txt");
The thing you're probably forgetting is the "/" there. Without that "/", it will look relative to your class' directory.
Alternatively, you could do this: ClassLoader.getSystemClassLoader().getResource("my.file.txt").getPath(). Notice the lack of a slash.
You are asking why this is happening and you are saying you want to load the resources.
The "why": see the other posts. No reason to duplicate them here.
The "how": the following code shows how to load the resources. Assuming they are in a file called "your.resources" and that this file is in the classpath; which, according to your post, it is.
import java.io.IOException;
import java.util.Properties;
public class Test {
public Test() throws IOException
{
final Properties properties = new Properties();
properties.load(this.getClass().getResourceAsStream("your.resources"));
System.out.println(properties);
}
public static void main(String[] args) throws IOException {
new Test();
}
}
Note that you don't need to provide the full path of the resources. As long as they are in the classpath, this will find them.