===== Using Resources in Java Programs =====
==== Overview ====
One commonly wants to use icons, configuration files, and other kinds of resources in Java programs.
While there are many ways to do accomplish, doing it in a way that is portable requires some thought
because of the way different IDEs and deployment mechanisms (e.g., [[ student:java:jar | .jar files ]])
organize code.
==== Icons ====
To easiest way to refer to icons in a portable fashion is to use a ''%%URL%%'' object (from the ''%%java.net%%'' package), and the easiest way to get the appropriate ''%%URL%%'' object is to use a ''%%Class%%'' object. For example, if you want to get the ''%%URL%%'' for a resource named ''%%logo.png%%'' that is in the ''%%icons%%'' directory **under the top-level directory containing the class of the object referred to by ''%%this%%''** then you would do the following:
URL url = this.getClass().getResource("/images/logo.png").
You could then load it as follows:
ImageIcon icon = new ImageIcon(url);
==== Resource Bundles ====
The static ''%%getBundle()%%'' method in the ''%%ResourceBundle%%'' class can be passed a ''%%ClassLoader%%'' that it will use to locate the resources. For example, if you want to get the ''%%ResourceBundle%%'' with the prefix ''%%Strings%%'' that is in the **top-level directory containing the class of the object referred to by ''%%this%%''** then you would do the following:
ResourceBundle strings;
strings = ResourceBundle.getBundle("Strings", Locale.getDefault(), this.getClass().getClassLoader());
==== Other Kinds of Resources ====
For other kinds of resources (e.g., configuration files), the easiest way to ensure that your code is portable is to use an ''%%InputStream%%'' object (from the ''%%java.io%%'' package) to refer to the resource and use a ''%%Class%%'' object to retrieve the appropriate ''%%InputStream%%'' object.
For example, if you want to get the ''%%InputStream%%'' for a text resource named ''%%default.cfg%%'' that is in the ''%%configurations%%'' directory **under the top-level directory containing the class of the object referred to by ''%%this%%''** then you would do the following:
InputStream is = this.getClass().getResourceAsStream("/configurations/config.txt").
You could then read the first line of this text resource as follows:
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String line = in.readLine();
==== Copying Resources to a Temporary Directory ====
Sometimes it is necessary to copy resources from a %%.jar%% file to a temporary directory on the files system (e.g., if you want to load %%.html%% files that are in a %%.jar%% file into a browser). The following utility class can be used for this purpose.
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
public class ResourceCopier {
/**
* Copy all of the files from a resources package to a temporary directory
* on the file system (whether or not the code and resources are in a .jar
* file or on the file system).
*
* This implementation assumes the resource package is a subpackage of the
* package that contains this class (though that can be changed_.
*
* @param id The ID to use for the temporary directory
* @param subdir The name of the subpackage that contains the resources
* @return The Path of the temporary directory
* @throws IOException If something goes wrong
* @throws URISyntaxException If something goes wrong
*/
public static Path copyResourcesToTemp(String id, String subdir)
throws IOException, URISyntaxException {
// Get the location of the ResourceCopier.class file
String canonicalName = ResourceCopier.class.getName();
String packageName = ResourceCopier.class.getPackageName();
String className = canonicalName.substring(packageName.length()+1) + ".class";
String thisLocation = ResourceCopier.class.getResource(className).toString();
// Remove the file name from the location and create a URI
int fileStart = thisLocation.indexOf(className);
String rootURL = thisLocation.substring(0, fileStart);
URI sourceURI = new URI(rootURL + subdir);
// Get the Path for source files (whether in a .jar file or the file system)
Path sourcePath;
FileSystem fileSystem = null;
if (sourceURI.getScheme().equals("jar")) {
fileSystem = FileSystems.newFileSystem(sourceURI, new HashMap());
sourcePath = fileSystem.getPath("/" + packageName + "/" + subdir);
} else {
sourcePath = Paths.get(sourceURI);
}
// Get a List of all of the files in the source Path
Stream files = Files.list(sourcePath);
List filesList = files.toList();
// Create a temporary directory on the local file system
Path temp = Files.createTempDirectory(id);
Path destinationPath = Path.of(temp.toString());
// Copy all of the files from the source Path to the temporary directory
for (Path file : filesList) {
Path targetFile = Path.of(destinationPath.toString(), file.getFileName().toString());
Files.copy(file, targetFile);
}
files.close();
if (fileSystem != null) fileSystem.close();
return destinationPath;
}
}
==== Under the Hood ====
These technique use the ''%%ClassLoader%%'' infrastructure to retrieve resources from the ''%%.jar%%'' file. So, it is very important that you understand the relative locations of the resource and the object that you call ''%%getClass()%%'' on. A leading ''%%/%%'' will start at the root of the directory tree, but you still must understand where in the ''%%.jar%%'' file is the root. Omitting the leading ''%%/%%'', on the other hand, will start at the location of the ''%%.class%%'' file of the object referred to by ''%%this%%''.