blob: 761328a4cbdf6d83c89975bc794b4471029b6b31 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.boot;
/**
* Platform URL support
*/
import java.net.*;
import java.io.*;
import java.util.*;
public abstract class PlatformURLConnection extends URLConnection {
// URL access
private boolean isInCache = false;
private boolean isJar = false;
// protected URL url; // declared in super (platform: URL)
private URL resolvedURL = null; // resolved file URL (eg. http: URL)
private URL cachedURL = null; // file URL in cache (file: URL)
private URLConnection connection = null; // actual connection
// local cache
private static Properties cacheIndex = new Properties();
private static String cacheLocation;
private static String indexName;
private static String filePrefix;
// constants
private static final int BUF_SIZE = 32768;
private static final Object NOT_FOUND = new Object(); // marker
private static final String CACHE_PROP = ".cache.properties"; //$NON-NLS-1$
private static final String CACHE_LOCATION_PROP = "location"; //$NON-NLS-1$
private static final String CACHE_INDEX_PROP = "index"; //$NON-NLS-1$
private static final String CACHE_PREFIX_PROP = "prefix"; //$NON-NLS-1$
private static final String CACHE_INDEX = ".index.properties"; //$NON-NLS-1$
private static final String CACHE_DIR = PlatformURLHandler.PROTOCOL + File.separator;
// debug tracing
public static boolean DEBUG = false;
public static boolean DEBUG_CONNECT = true;
public static boolean DEBUG_CACHE_LOOKUP = true;
public static boolean DEBUG_CACHE_COPY = true;
protected PlatformURLConnection(URL url) {
super(url);
}
protected boolean allowCaching() {
return false;
}
public void connect() throws IOException {
connect(false);
}
private synchronized void connect(boolean asLocal) throws IOException {
if (!connected) {
if (shouldCache(asLocal)) {
try {
URL inCache = getURLInCache();
if (inCache!=null) connection = inCache.openConnection();
} catch(IOException e) {
// failed to cache ... will use resolved URL instead
}
}
// use resolved URL
if (connection==null) connection = resolvedURL.openConnection();
connected = true;
if (DEBUG && DEBUG_CONNECT)
debug("Connected as "+connection.getURL()); //$NON-NLS-1$
}
}
private void copyToCache() throws IOException {
if (isInCache | cachedURL==null) return;
String tmp;
int ix;
// cache entry key
String key;
if (isJar) {
tmp = url.getFile();
ix = tmp.lastIndexOf(PlatformURLHandler.JAR_SEPARATOR);
if (ix!=-1) tmp = tmp.substring(0,ix);
key = tmp;
}
else key = url.getFile();
// source url
URL src;
if (isJar) {
tmp = resolvedURL.getFile();
ix = tmp.lastIndexOf(PlatformURLHandler.JAR_SEPARATOR);
if (ix!=-1) tmp = tmp.substring(0,ix);
src = new URL(tmp);
}
else src = resolvedURL;
InputStream srcis = null;
// cache target
String tgt;
if (isJar) {
tmp = cachedURL.getFile();
ix = tmp.indexOf(PlatformURLHandler.PROTOCOL_SEPARATOR);
if (ix!=-1) tmp = tmp.substring(ix+1);
ix = tmp.lastIndexOf(PlatformURLHandler.JAR_SEPARATOR);
if (ix!=-1) tmp = tmp.substring(0,ix);
tgt = tmp;
}
else tgt = cachedURL.getFile();
File tgtFile = null;
FileOutputStream tgtos = null;
boolean error = false;
long total = 0;
try {
if (DEBUG && DEBUG_CACHE_COPY) {
if (isJar) debug ("Caching jar as "+tgt); //$NON-NLS-1$
else debug("Caching as "+tgt); //$NON-NLS-1$
}
srcis = src.openStream();
byte[] buf = new byte[BUF_SIZE];
int count = srcis.read(buf);
tgtFile = new File(tgt);
tgtos = new FileOutputStream(tgtFile);
while(count!=-1) {
total += count;
tgtos.write(buf,0,count);
count = srcis.read(buf);
}
srcis.close();
srcis=null;
tgtos.close();
tgtos=null;
// add cache entry
cacheIndex.put(key,tgt);
isInCache = true;
}
catch(IOException e) {
error = true;
cacheIndex.put(key,NOT_FOUND); // mark cache entry for this execution
if (DEBUG && DEBUG_CACHE_COPY)
debug("Failed to cache due to "+e); //$NON-NLS-1$
throw e;
}
finally {
if (!error && DEBUG && DEBUG_CACHE_COPY)
debug(total + " bytes copied"); //$NON-NLS-1$
if (srcis!=null) srcis.close();
if (tgtos!=null) tgtos.close();
}
}
protected void debug(String s) {
System.out.println("URL "+getURL().toString()+"^"+Integer.toHexString(Thread.currentThread().hashCode())+" "+s); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
private static void debugStartup(String s) {
System.out.println("URL "+s); //$NON-NLS-1$
}
public URL[] getAuxillaryURLs () throws IOException {
return null;
}
public synchronized InputStream getInputStream() throws IOException {
if (!connected) connect();
return connection.getInputStream();
}
public URL getResolvedURL() {
return resolvedURL;
}
public URL getURLAsLocal() throws IOException {
connect(true); // connect and force caching if necessary
URL u = connection.getURL();
String up = u.getProtocol();
if (!up.equals(PlatformURLHandler.FILE) && !up.equals(PlatformURLHandler.JAR)) {
throw new IOException(Policy.bind("url.noaccess", url.toString())); //$NON-NLS-1$
}
return u;
}
private URL getURLInCache() throws IOException {
if (!allowCaching()) return null; // target should not be cached
if (isInCache) return cachedURL;
if (cacheLocation==null | cacheIndex==null) return null; // not caching
// check if we are dealing with a .jar/ .zip
String file = ""; //$NON-NLS-1$
String jarEntry = null;
if (isJar) {
file = url.getFile();
int ix = file.lastIndexOf(PlatformURLHandler.JAR_SEPARATOR);
if (ix!=-1) {
jarEntry = file.substring(ix+PlatformURLHandler.JAR_SEPARATOR.length());
file = file.substring(0,ix);
}
}
else {
file = url.getFile();
jarEntry = null;
}
// check for cached entry
String tmp = (String)cacheIndex.get(file);
// check for "not found" marker
if (tmp!=null && tmp==NOT_FOUND) throw new IOException();
// validate cache entry
if (tmp!=null && !(new File(tmp)).exists()) {
tmp = null;
cacheIndex.remove(url.getFile());
}
// found in cache
if (tmp!=null) {
if (isJar) {
if (DEBUG && DEBUG_CACHE_LOOKUP)
debug("Jar located in cache as "+tmp); //$NON-NLS-1$
tmp = PlatformURLHandler.FILE + PlatformURLHandler.PROTOCOL_SEPARATOR + tmp + PlatformURLHandler.JAR_SEPARATOR + jarEntry;
cachedURL = new URL(PlatformURLHandler.JAR,null,-1,tmp);
}
else {
if (DEBUG && DEBUG_CACHE_LOOKUP)
debug("Located in cache as "+tmp); //$NON-NLS-1$
cachedURL = new URL(PlatformURLHandler.FILE,null,-1,tmp);
}
isInCache = true;
}
else {
// attemp to cache
int ix = file.lastIndexOf("/"); //$NON-NLS-1$
tmp = file.substring(ix+1);
tmp = cacheLocation + filePrefix + Long.toString((new java.util.Date()).getTime()) + "_" + tmp; //$NON-NLS-1$
tmp = tmp.replace(File.separatorChar,'/');
if (isJar) {
tmp = PlatformURLHandler.FILE + PlatformURLHandler.PROTOCOL_SEPARATOR + tmp + PlatformURLHandler.JAR_SEPARATOR + jarEntry;
cachedURL = new URL(PlatformURLHandler.JAR,null,-1,tmp);
}
else cachedURL = new URL(PlatformURLHandler.FILE,null,-1,tmp);
copyToCache();
}
return cachedURL;
}
/*
* to be implemented by subclass
* @return URL resolved URL
*/
protected URL resolve() throws IOException {
throw new IOException();
}
private String resolvePath(String spec) {
if (spec.length() == 0 || spec.charAt(0) != '$')
return spec;
int i = spec.indexOf('/', 1);
String first = ""; //$NON-NLS-1$
String rest = ""; //$NON-NLS-1$
if (i == -1)
first = spec;
else {
first = spec.substring(0, i);
rest = spec.substring(i);
}
if (first.equalsIgnoreCase("$ws$")) //$NON-NLS-1$
return "ws/" + InternalBootLoader.getWS() + rest; //$NON-NLS-1$
if (first.equalsIgnoreCase("$os$")) //$NON-NLS-1$
return "os/" + InternalBootLoader.getOS() + rest; //$NON-NLS-1$
if (first.equalsIgnoreCase("$nl$")) { //$NON-NLS-1$
String nl = InternalBootLoader.getNL();
nl = nl.replace('_', '/');
return "nl/" + nl + rest; //$NON-NLS-1$
}
return spec;
}
protected String getId(String spec) {
int i = spec.lastIndexOf('_');
return i >= 0 ? spec.substring(0, i) : spec;
}
protected String getVersion(String spec) {
int i = spec.lastIndexOf('_');
return i >= 0 ? spec.substring(i + 1, spec.length()) : ""; //$NON-NLS-1$
}
void setResolvedURL(URL url) throws IOException {
if (resolvedURL==null) {
int ix = url.getFile().lastIndexOf(PlatformURLHandler.JAR_SEPARATOR);
isJar = -1 != ix;
// Resolved URLs containing !/ separator are assumed to be jar URLs.
// If the resolved protocol is not jar, new jar URL is created.
if (isJar && !url.getProtocol().equals(PlatformURLHandler.JAR))
url = new URL(PlatformURLHandler.JAR,"",-1,url.toExternalForm()); //$NON-NLS-1$
resolvedURL=url;
}
}
private boolean shouldCache(boolean asLocal) {
// don't cache files that are known to be local
String rp = resolvedURL.getProtocol();
String rf = resolvedURL.getFile();
if (rp.equals(PlatformURLHandler.FILE)) return false;
if (rp.equals(PlatformURLHandler.JAR) && (rf.startsWith(PlatformURLHandler.FILE))) return false;
// for other files force caching if local connection was requested
if (asLocal) return true;
// for now cache all files
// XXX: add cache policy support
return true;
}
static void shutdown() {
if (indexName!=null && cacheLocation!=null) {
// weed out "not found" entries
Enumeration keys = cacheIndex.keys();
String key;
Object value;
while (keys.hasMoreElements()) {
key = (String) keys.nextElement();
value = cacheIndex.get(key);
if (value==NOT_FOUND) cacheIndex.remove(key);
}
//if the cache index is empty we don't need to save it
if (cacheIndex.size() == 0)
return;
try {
// try to save cache index
FileOutputStream fos = null;
fos = new FileOutputStream(cacheLocation+indexName);
try {
cacheIndex.store(fos,null);
} finally {
fos.close();
}
}
catch(IOException e) {
// failed to store cache index ... ignore
}
}
}
static void startup(String location) {
verifyLocation(location); // check for platform location, ignore errors
String cacheProps = location.trim();
if (!cacheProps.endsWith(File.separator)) cacheProps += File.separator;
cacheProps += CACHE_PROP;
File cachePropFile = new File(cacheProps);
Properties props = null;
FileInputStream fis;
if (cachePropFile.exists()) {
// load existing properties
try {
props = new Properties();
fis = new FileInputStream(cachePropFile);
try {
props.load(fis);
}
finally {
fis.close();
}
}
catch(IOException e) {
props = null;
}
}
if (props==null) {
// first time up, or failed to load previous settings
props = new Properties();
String tmp = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
if (!tmp.endsWith(File.separator)) tmp += File.separator;
tmp += CACHE_DIR;
props.put(CACHE_LOCATION_PROP,tmp);
tmp = Long.toString((new java.util.Date()).getTime());
props.put(CACHE_PREFIX_PROP,tmp);
tmp += CACHE_INDEX;
props.put(CACHE_INDEX_PROP,tmp);
// save for next time around
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cachePropFile);
try {
props.store(fos,null);
}
finally {
fos.close();
}
}
catch(IOException e) {
// failed to store cache location metadata ... ignore
}
}
// remember settings for shutdown processing
filePrefix = (String)props.get(CACHE_PREFIX_PROP);
indexName = (String)props.get(CACHE_INDEX_PROP);
cacheLocation = (String)props.get(CACHE_LOCATION_PROP);
if (DEBUG) {
debugStartup("Cache location: " + cacheLocation); //$NON-NLS-1$
debugStartup("Cache index: " + indexName); //$NON-NLS-1$
debugStartup("Cache file prefix: " + filePrefix); //$NON-NLS-1$
}
// create cache directory structure if needed
if (!verifyLocation(cacheLocation)) {
indexName = null;
cacheLocation = null;
if (DEBUG)
debugStartup("Failed to create cache directory structure. Caching suspended"); //$NON-NLS-1$
return;
}
// attempt to initialize cache index
if (cacheLocation!=null && indexName!=null) {
try {
fis = new FileInputStream(cacheLocation+indexName);
try {
cacheIndex.load(fis);
}
finally {
fis.close();
}
}
catch(IOException e) {
if (DEBUG)
debugStartup("Failed to initialize cache"); //$NON-NLS-1$
}
}
}
private static boolean verifyLocation(String location) {
// verify cache directory exists. Create if needed
File cacheDir = new File(location);
if (cacheDir.exists())
return true;
return cacheDir.mkdirs();
}
}