===== 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().getResource("/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%%''.