From e400ba5be619a27ba3d27ea3b44c0629b8d50bdf Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Mon, 13 Jan 2003 22:50:36 +0000 Subject: [PATCH] Resources are unpacked and cached in a temporary directory on the filesystem. This should speed up subsequent loads as they won't have to be decompressed. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2117 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/resource/ResourceBundle.java | 80 ++++++++++++++++-- .../threerings/resource/ResourceManager.java | 82 ++++++++++++++++++- 2 files changed, 152 insertions(+), 10 deletions(-) diff --git a/src/java/com/threerings/resource/ResourceBundle.java b/src/java/com/threerings/resource/ResourceBundle.java index 0688ec5c7..ac77b9589 100644 --- a/src/java/com/threerings/resource/ResourceBundle.java +++ b/src/java/com/threerings/resource/ResourceBundle.java @@ -1,9 +1,13 @@ // -// $Id: ResourceBundle.java,v 1.4 2002/08/19 23:31:48 mdb Exp $ +// $Id: ResourceBundle.java,v 1.5 2003/01/13 22:50:36 mdb Exp $ package com.threerings.resource; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -11,6 +15,9 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.samskivert.io.NestableIOException; +import com.samskivert.util.StringUtil; + +import org.apache.commons.io.StreamUtils; /** * A resource bundle provides access to the resources in a jar file. @@ -51,16 +58,53 @@ public class ResourceBundle */ public InputStream getResource (String path) throws IOException + { + // unpack our resources into a temp directory so that we can load + // them quickly and the file system can cache them sensibly + File rfile = getResourceFile(path); + return (rfile == null) ? null : new FileInputStream(rfile); + } + + /** + * Returns a file from which the specified resource can be loaded. + * This method will unpack the resource into a temporary directory and + * return a reference to that file. + * + * @param path the path to the resource in this jar file. + * + * @return a file from which the resource can be loaded or null if no + * such resource exists. + */ + public File getResourceFile (String path) + throws IOException { resolveJarFile(); - // TBD: determine whether or not we need to convert the path into - // a platform-dependent path if we're on Windows - JarEntry entry = _jarSource.getJarEntry(path); - InputStream stream = null; - if (entry != null) { - stream = _jarSource.getInputStream(entry); + + // compute the path to our temporary file + String tpath = StringUtil.md5hex(_source.getPath() + "%" + path); + File tfile = new File(_tmpdir, tpath); + if (tfile.exists()) { + return tfile; } - return stream; + + // make sure said resource exists in the first place + JarEntry entry = _jarSource.getJarEntry(path); + if (entry == null) { + return null; + } + + // clean up our unpacked files when the JVM exits +// tfile.deleteOnExit(); + + // copy the resource into the temporary file + BufferedOutputStream fout = + new BufferedOutputStream(new FileOutputStream(tfile)); + InputStream jin = _jarSource.getInputStream(entry); + StreamUtils.pipe(jin, fout); + jin.close(); + fout.close(); + + return tfile; } /** @@ -117,6 +161,26 @@ public class ResourceBundle /** The file from which we construct our jar file. */ protected File _source; + /** The directory into which our contents are unpacked, if we are + * unpacked. */ + protected File _unpackDir; + /** The jar file from which we load resources. */ protected JarFile _jarSource; + + /** A directory in which we temporarily unpack our resource files. */ + protected static File _tmpdir; + + static { + String tmpdir = System.getProperty("java.io.tmpdir"); + if (tmpdir == null) { + Log.info("No system defined temp directory. Faking it."); + tmpdir = System.getProperty("user.dir"); + } + _tmpdir = new File(tmpdir, ".narcache"); + if (!_tmpdir.exists()) { + Log.info("Creating narya temp cache directory '" + _tmpdir + "'."); + _tmpdir.mkdir(); + } + } } diff --git a/src/java/com/threerings/resource/ResourceManager.java b/src/java/com/threerings/resource/ResourceManager.java index 57285755d..ef59c3bbd 100644 --- a/src/java/com/threerings/resource/ResourceManager.java +++ b/src/java/com/threerings/resource/ResourceManager.java @@ -1,8 +1,9 @@ // -// $Id: ResourceManager.java,v 1.18 2002/11/20 02:00:36 mdb Exp $ +// $Id: ResourceManager.java,v 1.19 2003/01/13 22:50:36 mdb Exp $ package com.threerings.resource; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -21,6 +22,10 @@ import java.util.List; import java.util.Properties; import java.util.StringTokenizer; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; + import com.samskivert.util.StringUtil; import com.threerings.resource.DownloadManager.DownloadDescriptor; @@ -486,6 +491,40 @@ public class ResourceManager throw new FileNotFoundException(errmsg); } + /** + * Fetches the specified resource as an {@link ImageInputStream} and + * one that takes advantage, if possible, of caching of unpacked + * resources on the local filesystem. + * + * @exception FileNotFoundException thrown if the resource could not + * be located in any of the bundles in the specified set, or if the + * specified set does not exist. + * @exception IOException thrown if a problem occurs locating or + * reading the resource. + */ + public ImageInputStream getImageResource (String path) + throws IOException + { + // first look for this resource in our default resource bundle + for (int i = 0; i < _default.length; i++) { + File file = _default[i].getResourceFile(path); + if (file != null) { + return new FileImageInputStream(file); + } + } + + // if we didn't find anything, try the classloader + String rpath = _rootPath + path; + InputStream in = _loader.getResourceAsStream(rpath); + if (in != null) { + return new MemoryCacheImageInputStream(new BufferedInputStream(in)); + } + + // if we still haven't found it, we throw an exception + String errmsg = "Unable to locate image resource [path=" + path + "]"; + throw new FileNotFoundException(errmsg); + } + /** * Returns an input stream from which the requested resource can be * loaded. Note: this performs a linear search of all of the @@ -518,7 +557,7 @@ public class ResourceManager if (instr != null) { // Log.info("Found resource [rset=" + rset + // ", bundle=" + bundles[ii].getSource().getPath() + -// ", path=" + path + "]."); +// ", path=" + path + ", in=" + instr + "]."); return instr; } } @@ -527,6 +566,45 @@ public class ResourceManager "Unable to locate resource [set=" + rset + ", path=" + path + "]"); } + /** + * Fetches the specified resource as an {@link ImageInputStream} and + * one that takes advantage, if possible, of caching of unpacked + * resources on the local filesystem. + * + * @exception FileNotFoundException thrown if the resource could not + * be located in any of the bundles in the specified set, or if the + * specified set does not exist. + * @exception IOException thrown if a problem occurs locating or + * reading the resource. + */ + public ImageInputStream getImageResource (String rset, String path) + throws IOException + { + // grab the resource bundles in the specified resource set + ResourceBundle[] bundles = getResourceSet(rset); + if (bundles == null) { + throw new FileNotFoundException( + "Unable to locate image resource [set=" + rset + + ", path=" + path + "]"); + } + + // look for the resource in any of the bundles + int size = bundles.length; + for (int ii = 0; ii < size; ii++) { + File file = bundles[ii].getResourceFile(path); + if (file != null) { +// Log.info("Found image resource [rset=" + rset + +// ", bundle=" + bundles[ii].getSource() + +// ", path=" + path + ", file=" + file + "]."); + return new FileImageInputStream(file); + } + } + + String errmsg = "Unable to locate image resource [set=" + rset + + ", path=" + path + "]"; + throw new FileNotFoundException(errmsg); + } + /** * Returns a reference to the resource set with the specified name, or * null if no set exists with that name. Services that wish to load