📄 cache.java
字号:
package de.fhm.jkf.launch.cl;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import de.fhm.jkf.launch.clsv.ArchiveInfo;
import de.fhm.jkf.launch.clsv.JarFilenameFilter;
import de.fhm.jkf.launch.clsv.LaunchLogger;
/**
* $Log: Cache.java,v $
* Revision 1.3 2003/02/25 17:16:23 willaxt
* updated javadoc comments
* reformatted source code
*
* Revision 1.2 2003/02/17 14:55:12 willaxt
* renamed map instance variable to classMap for better understanding of the code
*
* Revision 1.1 2003/01/16 13:10:18 mwulff
* initial version
*
*
* @author Theodor Willax
*
* <br><br><center><table border="1" width="80%"><hr>
* <strong><a href="http://jkf.sourceforge.net">The JKF Project</a></strong>
* <p>
* Copyright (C) 2002 by Theodor Willax
* <p>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <p>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* <p>
* You should have received a copy of the <a href="http://www.gnu.org/copyleft/lesser.html">
* GNU Lesser General Public License</a> along with this library; if not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
* <hr></table></center>
*
* A simple cache for jar archives of an application. Simple means, it does only
* store the complete archives on the local filesystem. No special sorting or
* arrangement is done.
* <br>
* It checks the <tt>Implementation-Version</tt> attribute of manifest files in the archives.
* If no <tt>Implementation-Version</tt> is available, the archive is not stored, respectively
* deleted from the <code>Cache</code> to have not outdated versions available.
*
*/
public class Cache {
/**
* The direcotry this <code>Cache</code> lives in.
*/
private File cacheDir = null;
/**
* This <code>Map</code> contains all classname - <code>byte</code>S
* mappings. This means if you query this <code>Map</code> for
* a classname, it returns the <code>ByteArray</code> the class
* consists of.
*/
private Map classMap = null;
/**
* Creates a new <code>Cache</code> for the given application.
* The <code>Cache</code> resides in the users home directory
* as given by the java property <code>user.home</code> under
* the directory <b>".jkf/appName"</b>.
*
* @param appName The name of the application this <code>Cache</code>
* is for.
*
* @throws IOException if the cache can not be created or is not writeable.
*/
public Cache(String appName) throws IOException {
if (appName == null) {
throw new IllegalArgumentException("appName null not allowed");
}
cacheDir =
new File(
System.getProperty("user.home")
+ File.separator
+ ".jkf"
+ File.separator
+ appName);
if (cacheDir.isDirectory() == false) {
if (cacheDir.mkdirs() == false) {
throw new IOException(
"cache directory could not be created: "
+ cacheDir.getAbsolutePath());
}
}
if (cacheDir.canWrite() == false) {
throw new IOException(
"cache directory not writeable: " + cacheDir.getAbsolutePath());
}
classMap = new HashMap();
}
/**
* Initializes the <code>Cache</code> with the available
* archives. Compares the version info from the archives
* in the <code>Cache</code> with the versions on the
* list. If an old version lives in the <code>Cache</code>,
* no classes from it go into the <code>Map</code>. If
* a class from one of this old archives is wanted,
* <code>null</code> is returned. Removes archives from
* the cache which are no longer listet in the archive
* info list.
* <br>
* If no version information at all is found, we delete the
* archive from the cache. So we are sure we have no old
* outdated versions in the <code>Cache</code>.
* <br>
* Repeated calls to this method initialze the <code>Cache</code>
* repeatedly.
*
* @param archives <code>Map</code> of archives an their version
*
* @throws IOException if something with the file handling in the
* cache doesn't work.
*/
public void init(List archiveInfo) throws IOException {
LaunchLogger.debug(getClass().getName() + ".init() called");
classMap.clear();
File[] jars = cacheDir.listFiles(new JarFilenameFilter());
for (int i = 0; i < jars.length; i++) {
// @todo better version info compare logics
String localVersion = getVersion(jars[i].getName());
String remoteVersion = null;
Iterator it = archiveInfo.iterator();
while (it.hasNext()) {
ArchiveInfo ai = (ArchiveInfo) it.next();
if (ai.getName().equalsIgnoreCase(jars[i].getName())) {
remoteVersion = ai.getVersion();
break;
}
}
if ((localVersion != null)
&& (localVersion.equalsIgnoreCase(remoteVersion))) {
addToMap(jars[i]);
} else {
// delete from cache, to get rid of old unused archives
get(jars[i].getName());
LaunchLogger.info(
"removed " + jars[i].getName() + " from cache");
}
}
}
/**
* Adds given archive to the <code>Map</code>. Replaces
* any old classes with new ones if duplicates are in
* this archive.
*
* @param arch The archive which classes are added to
* the <code>Map</code>.
* @exception IOException if I/O errors occur during
* handling with the archive.
*/
private void addToMap(File arch) throws IOException {
JarFile jf = new JarFile(arch);
Enumeration enum = jf.entries();
while (enum.hasMoreElements()) {
ZipEntry ze = (ZipEntry) enum.nextElement();
InputStream in = jf.getInputStream(ze);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] buf = new byte[4096];
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// make class loader class name out of jar file name
String cn = ze.getName();
if (cn.endsWith(".class")) {
cn = cn.replace('/', '.');
cn = cn.substring(0, cn.lastIndexOf(".class"));
}
LaunchLogger.debug("className: " + cn);
// replaces old entry!
classMap.put(cn, new ByteArray(out.toByteArray()));
out.close();
in.close();
}
}
/**
* Puts this archive into the cache. Replaces any old copy.
*
* @param arch The archive to put into the cache.
*
* @throws FileNotFoundException if the name of the <code>Archive</code>
* is a directory or is not writeable due to any case.
*
* @throws IOException if an I/O error occurs during writing the
* <code>Archive</code>
*/
public void put(Archive arch) throws FileNotFoundException, IOException {
File f =
new File(
cacheDir.getAbsolutePath() + File.separator + arch.getName());
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
out.write(arch.getBytes());
out.close();
// put class bytes in classMap
addToMap(f);
}
/**
* Puts an archive which contains configuration files
* into the cache directory.
*
* @param arch java archive which contains configuration files.
*/
public void putConfiguration(Archive arch)
throws FileNotFoundException, IOException {
JarInputStream jin =
new JarInputStream(new ByteArrayInputStream(arch.getBytes()));
JarEntry je;
int len = 0;
byte buf[] = new byte[1024];
while ((je = jin.getNextJarEntry()) != null) {
File f =
new File(
cacheDir.getAbsolutePath() + File.separator + je.getName());
BufferedOutputStream out =
new BufferedOutputStream(new FileOutputStream(f));
while ((len = jin.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
jin.closeEntry();
}
jin.close();
}
/**
* Retrieves the archive from the cache. If it is not found,
* <code>null</code> is returned. It also deletes the archive
* from the cache.
*
* @param arch The archive to search for.
* @return The <code>Archive</code> if found, <code>null</code> otherwise.
* @throws FileNotFoundException if the name of the <code>Archive</code>
* is a directory or is not readable due to any case.
*
* @throws IOException if an I/O error occurs during reading or deleting the
* <code>Archive</code>
*/
public Archive get(String arch) throws FileNotFoundException, IOException {
File f = new File(cacheDir.getAbsolutePath() + File.separator + arch);
if (f.isFile()) {
byte[] b = new byte[(int) f.length()];
InputStream in = new BufferedInputStream(new FileInputStream(f));
in.read(b);
in.close();
if (f.delete() == false) {
throw new IOException(arch + " could not be deleted");
}
return new Archive(arch, b);
}
// no valid file ==> no archive returned
return null;
}
/**
* Returns the <code>File</code> object for the given archive
* name.
*
* @param arch The archive name.
* @return the <code>File</code> object for the archive name.
*/
public File getFile(String arch) throws FileNotFoundException {
return new File(cacheDir.getAbsolutePath() + File.separator + arch);
}
/**
* Retrieves the version of an archive. The version information
* is taken from the version entry in the MANIFEST.MF file in
* the jar archive. If the archive is not in the cache or no
* MANIFEST.MF is found, <code>null</code> is returned.
*
* The version string must be in the entry
* <code>Implementation-Version</code> of the main attributes
* of the MANIFEST.MF file.
*
* @param arch The archive from which the version info is wanted
* @return the version info of the archive if available,
* <code>null</code> otherwise.
* @throws FileNotFoundException if the name of the <code>Archive</code>
* is a directory or is not readable due to any case.
*
* @throws IOException if an I/O error occurs during reading the
* <code>Archive</code>
*/
public String getVersion(String arch)
throws FileNotFoundException, IOException {
File f = new File(cacheDir.getAbsolutePath() + File.separator + arch);
JarInputStream in =
new JarInputStream(new BufferedInputStream(new FileInputStream(f)));
Manifest mf = in.getManifest();
if (mf != null) {
Attributes a = mf.getMainAttributes();
return a.getValue("Implementation-Version");
}
return null;
}
/**
* Searches for the given class in the <code>Cache</code>. If
* not found, <code>null</code> is returned. If found, an
* <code>ByteArray</code> is delivered.
*
* @param name the class to search for
* @return the <code>ByteArray</code> if found, <code>null</code> otherwise.
*/
public ByteArray getBytesForClass(String name) {
return (ByteArray) classMap.get(name);
}
/**
* Clears this <code>Cache</code>. Removes the files from
* the local file system.
* */
public void clear() {
classMap.clear();
File[] files = cacheDir.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].delete() == false) {
LaunchLogger.error(
files[i].getName()
+ " could not be deleted from local cache");
}
}
}
/**
* Returns true if this <code>Cache</code> is empty.
*
* @return boolean <code>true</code> if this <code>Cache</code> is empty,
* <code>false</code> otherwise.
*/
public boolean isEmtpy() {
File[] files = cacheDir.listFiles();
return files.length == 0;
}
/**
* Returns the directory this <code>Cache</code> lives in.
*
* @returnString the dir this <code>Cache</code> lives in.
*/
public String getCacheDir() {
return cacheDir.getAbsolutePath();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -