// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
//
// 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.
//
// 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.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
package net.sourceforge.jnlp.cache;
import java.io.*;
import java.net.*;
import java.util.*;
import net.sourceforge.jnlp.*;
import net.sourceforge.jnlp.runtime.*;
import net.sourceforge.jnlp.util.*;
/**
* Information about a single resource to download.
* This class tracks the downloading of various resources of a
* JNLP file to local files. It can be used to download icons,
* jnlp and extension files, jars, and jardiff files using the
* version based protocol or any file using the basic download
* protocol.
*
* Resources can be put into download groups by specifying a part
* name for the resource. The resource tracker can also be
* configured to prefetch resources, which are downloaded in the
* order added to the media tracker.
*
* @author Jon A. Maxwell (JAM) - initial author
* @version $Revision: 1.9 $
*/
public class Resource {
// todo: fix resources to handle different versions
// todo: IIRC, any resource is checked for being up-to-date
// only once, regardless of UpdatePolicy. verify and fix.
/** status bits */
public static final int UNINITIALIZED = 0;
public static final int CONNECT = 1;
public static final int CONNECTING = 2;
public static final int CONNECTED = 4;
public static final int DOWNLOAD = 8;
public static final int DOWNLOADING = 16;
public static final int DOWNLOADED = 32;
public static final int ERROR = 64;
public static final int STARTED = 128; // enqueued or being worked on
/** list of weak references of resources currently in use */
private static WeakList resources = new WeakList();
/** weak list of trackers monitoring this resource */
private WeakList trackers = new WeakList();
/** the remote location of the resource */
URL location;
/** the location to use when downloading */
private URL downloadLocation;
/** the local file downloaded to */
File localFile;
/** the requested version */
Version requestVersion;
/** the version downloaded from server */
Version downloadVersion;
/** amount in bytes transferred */
long transferred = 0;
/** total size of the resource, or -1 if unknown */
long size = -1;
/** the status of the resource */
int status = UNINITIALIZED;
/** Update policy for this resource */
UpdatePolicy updatePolicy;
/**
* Create a resource.
*/
private Resource(URL location, Version requestVersion, UpdatePolicy updatePolicy) {
this.location = location;
this.downloadLocation = location;
this.requestVersion = requestVersion;
this.updatePolicy = updatePolicy;
}
/**
* Return a shared Resource object representing the given
* location and version.
*/
public static Resource getResource(URL location, Version requestVersion, UpdatePolicy updatePolicy) {
synchronized (resources) {
Resource resource = new Resource(location, requestVersion, updatePolicy);
int index = resources.indexOf(resource);
if (index >= 0) { // return existing object
Resource result = resources.get(index);
if (result != null)
return result;
}
resources.add(resource);
resources.trimToSize();
return resource;
}
}
/**
* Returns the remote location of the resource.
*/
public URL getLocation() {
return location;
}
/**
* Returns the URL to use for downloading the resource. This can be
* different from the original location since it may use a different
* file name to support versioning and compression
* @return the url to use when downloading
*/
public URL getDownloadLocation() {
return downloadLocation;
}
/**
* Set the url to use for downloading the resource
* @param location
*/
public void setDownloadLocation(URL location) {
downloadLocation = location;
}
/**
* Returns the tracker that first created or monitored the
* resource, or null if no trackers are monitoring the resource.
*/
ResourceTracker getTracker() {
synchronized (trackers) {
List t = trackers.hardList();
if (t.size() > 0)
return t.get(0);
return null;
}
}
/**
* Returns true if any of the specified flags are set.
*/
public boolean isSet(int flag) {
if (flag == UNINITIALIZED)
return status == UNINITIALIZED;
else
return (status & flag) != 0;
}
/**
* Returns the update policy for this resource
*
* @return The update policy
*/
public UpdatePolicy getUpdatePolicy() {
return this.updatePolicy;
}
/**
* Returns a human-readable status string.
*/
private String getStatusString(int flag) {
StringBuffer result = new StringBuffer();
if (flag == 0)
result.append("<> ");
if ((flag & CONNECT) != 0)
result.append("CONNECT ");
if ((flag & CONNECTING) != 0)
result.append("CONNECTING ");
if ((flag & CONNECTED) != 0)
result.append("CONNECTED ");
if ((flag & DOWNLOAD) != 0)
result.append("DOWNLOAD ");
if ((flag & DOWNLOADING) != 0)
result.append("DOWNLOADING ");
if ((flag & DOWNLOADED) != 0)
result.append("DOWNLOADED ");
if ((flag & ERROR) != 0)
result.append("ERROR ");
if ((flag & STARTED) != 0)
result.append("STARTED ");
return result.deleteCharAt(result.length() - 1).toString();
}
/**
* Changes the status by clearing the flags in the first
* parameter and setting the flags in the second. This method
* is synchronized on this resource.
*/
public void changeStatus(int clear, int add) {
int orig = 0;
synchronized (this) {
orig = status;
this.status &= ~clear;
this.status |= add;
}
if (JNLPRuntime.isDebug())
if (status != orig) {
System.out.print("Status: " + getStatusString(status));
if ((status & ~orig) != 0)
System.out.print(" +(" + getStatusString(status & ~orig) + ")");
if ((~status & orig) != 0)
System.out.print(" -(" + getStatusString(~status & orig) + ")");
System.out.println(" @ " + location.getPath());
}
}
/**
* Removes the tracker to the list of trackers monitoring this
* resource.
*/
public void removeTracker(ResourceTracker tracker) {
synchronized (trackers) {
trackers.remove(tracker);
trackers.trimToSize();
}
}
/**
* Adds the tracker to the list of trackers monitoring this
* resource.
*/
public void addTracker(ResourceTracker tracker) {
synchronized (trackers) {
// prevent GC between contains and add
List t = trackers.hardList();
if (!t.contains(tracker))
trackers.add(tracker);
trackers.trimToSize();
}
}
/**
* Instructs the trackers monitoring this resource to fire a
* download event.
*/
protected void fireDownloadEvent() {
List send;
synchronized (trackers) {
send = trackers.hardList();
}
for (int i = 0; i < send.size(); i++) {
ResourceTracker rt = send.get(i);
rt.fireDownloadEvent(this);
}
}
public boolean equals(Object other) {
if (other instanceof Resource) {
// this prevents the URL handler from looking up the IP
// address and doing name resolution; much faster so less
// time spent in synchronized addResource determining if
// Resource is already in a tracker, and better for offline
// mode on some OS.
return CacheUtil.urlEquals(location, ((Resource) other).location);
}
return false;
}
public String toString() {
return "location=" + location.toString() + " state=" + getStatusString(status);
}
}