Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* ZipCache.java
*
* Copyright (C) 2010, 2014 Mark Evenson
* $Id$
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from
* or based on this library. If you modify this library, you may extend
* this exception to your version of the library, but you are not
* obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package org.armedbear.lisp;
import org.armedbear.lisp.util.HttpHead;
import static org.armedbear.lisp.Lisp.*;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
/**
* A cache for all zip/jar file accesses by URL that uses the last
* modified time of the cached resource.
*
* This implementation is synchronized on accesses via get().
* Usage without multiple threads recompiling code that is then
* re-loaded should be fine.
*
* If you run into problems with caching, use
* (SYS::DISABLE-ZIP-CACHE). Once disabled, the caching cannot be
* re-enabled.
*
*/
public class ZipCache {
// To make this thread safe, we should return a proxy for ZipFile
// that keeps track of the number of outstanding references handed
// out, not allowing ZipFile.close() to succeed until that count
// has been reduced to 1 or the finalizer is executing.
// Unfortunately the relatively simple strategy of extending
// ZipFile via a CachedZipFile does not work because there is not
// a null arg constructor for ZipFile.
static class Entry {
long lastModified;
ZipFile file;
}
static boolean cacheEnabled = true;
private final static Primitive DISABLE_ZIP_CACHE = new disable_zip_cache();
final static class disable_zip_cache extends Primitive {
disable_zip_cache() {
super("disable-zip-cache", PACKAGE_SYS, true, "",
"Disable all caching of ABCL FASLs and ZIPs.");
}
@Override
public LispObject execute() {
ZipCache.disable();
return T;
}
}
static public synchronized void disable() {
cacheEnabled = false;
zipCache.clear();
}
static HashMap zipCache = new HashMap();
synchronized public static ZipFile get(Pathname p) {
return get(Pathname.makeURL(p));
}
static final SimpleDateFormat ASCTIME
= new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US);
static final SimpleDateFormat RFC_1036
= new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US);
static final SimpleDateFormat RFC_1123
= new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
synchronized public static ZipFile get(final URL url) {
if (!cacheEnabled) {
if (url.getProtocol().equals("file")) {
File f = new File(url.getPath());
try {
return new ZipFile(f);
} catch (ZipException e) {
error(new FileError("Failed to construct ZipFile"
+ " because " + e,
Pathname.makePathname(f)));
} catch (IOException e) {
error(new FileError("Failed to contruct ZipFile"
+ " because " + e,
Pathname.makePathname(f)));
}
} else {
Entry e = fetchURL(url, false);
return e.file;
}
}
Entry entry = zipCache.get(url);
// Check that the cache entry still accesses a valid ZipFile
if (entry != null) {
// Simplest way to call private ZipFile.ensureOpen()
try {
int size = entry.file.size();
} catch (IllegalStateException e) {
zipCache.remove(url);
entry = null;
}
}
if (entry != null) {
if (url.getProtocol().equals("file")) {
File f = new File(url.getPath());
long current = f.lastModified();
if (current > entry.lastModified) {
try {
entry.file = new ZipFile(f);
entry.lastModified = current;
} catch (IOException e) {
Debug.trace(e.toString()); // XXX
}
}
} else if (url.getProtocol().equals("http")) {
// Unfortunately, the Apple JDK under OS X doesn't do
// HTTP HEAD requests, instead refetching the entire
// resource, and I assume this is the case in all
// Sun-derived JVMs. So, we use a custom HEAD
// implementation only looking for Last-Modified
// headers, which if we don't find, we give up and
// refetch the resource.
String dateString = null;
try {
dateString = HttpHead.get(url, "Last-Modified");
} catch (IOException ex) {
Debug.trace(ex);
}
Date date = null;
ParsePosition pos = new ParsePosition(0);
if (dateString != null) {
date = RFC_1123.parse(dateString, pos);
if (date == null) {
date = RFC_1036.parse(dateString, pos);
if (date == null)
date = ASCTIME.parse(dateString, pos);
}
}
if (date == null || date.getTime() > entry.lastModified) {
entry = fetchURL(url, false);
zipCache.put(url, entry);
}
if (date == null) {
if (dateString == null)
Debug.trace("Failed to retrieve request header: "
+ url.toString());
else
Debug.trace("Failed to parse Last-Modified date: " +
dateString);
}
} else {
entry = fetchURL(url, false);
zipCache.put(url, entry);
}
} else {
if (url.getProtocol().equals("file")) {
entry = new Entry();
String path = url.getPath();
if (Utilities.isPlatformWindows) {
String authority = url.getAuthority();
if (authority != null) {
path = authority + path;
}
}
File f = new File(path);
entry.lastModified = f.lastModified();
try {
entry.file = new ZipFile(f);
} catch (ZipException e) {
error(new FileError("Failed to get cached ZipFile"
+ " because " + e,
Pathname.makePathname(f)));
} catch (IOException e) {
error(new FileError("Failed to get cached ZipFile"
+ " because " + e,
Pathname.makePathname(f)));
}
} else {
entry = fetchURL(url, true);
}
zipCache.put(url, entry);
}
return entry.file;
}
static private Entry fetchURL(URL url, boolean cached) {
Entry result = new Entry();
URL jarURL = null;
try {
jarURL = new URL("jar:" + url + "!/");
} catch (MalformedURLException e) {
error(new LispError("Failed to form a jar: URL from "
+ "'" + url + "'"
+ " because " + e));
}
URLConnection connection = null;
try {
connection = jarURL.openConnection();
} catch (IOException e) {
error(new LispError("Failed to open "
+ "'" + jarURL + "'"
+ " with exception "
+ e));
}
if (!(connection instanceof JarURLConnection)) {
error(new LispError("Could not get a URLConnection from "
+ "'" + jarURL + "'"));
}
JarURLConnection jarURLConnection = (JarURLConnection) connection;
jarURLConnection.setUseCaches(cached);
try {
result.file = jarURLConnection.getJarFile();
} catch (IOException e) {
error(new LispError("Failed to fetch URL "
+ "'" + jarURLConnection + "'"
+ " because " + e));
}
result.lastModified = jarURLConnection.getLastModified();
return result;
}
// ## remove-zip-cache-entry pathname => boolean
private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
private static class remove_zip_cache_entry extends Primitive {
remove_zip_cache_entry() {
super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
}
@Override
public LispObject execute(LispObject arg) {
Pathname p = coerceToPathname(arg);
boolean result = ZipCache.remove(p);
return result ? T : NIL;
}
}
synchronized public static boolean remove(URL url) {
Entry entry = zipCache.get(url);
if (entry != null) {
try {
entry.file.close();
} catch (IOException e) {}
zipCache.remove(entry);
return true;
}
return false;
}
synchronized public static boolean remove(Pathname p) {
URL url = Pathname.makeURL(p);
if (url == null) {
return false;
}
return ZipCache.remove(url);
}
synchronized public static boolean remove(File f) {
Pathname p = Pathname.makePathname(f);
return ZipCache.remove(p);
}
}