package NET.worlds.network; import NET.worlds.core.IniFile; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URLConnection; import java.net.UnknownHostException; import java.util.Date; import java.util.Observer; import java.util.Vector; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; public class CacheEntry implements Runnable, Serializable { private static final long serialVersionUID = 4845526825815464122L; static boolean httpFaulted = IniFile.override().getIniInt("Offline", 0) == 1 || IniFile.gamma().getIniInt("Offline", 0) == 1; static boolean stopOnFault = IniFile.gamma().getIniInt("StopOnHttpFault", 0) == 1; static final int CACHE_MAX_ENTRIES = IniFile.gamma().getIniInt("NetCacheEntries", 1000); static final int MAX_THREADS = IniFile.gamma().getIniInt("NetCacheThreads", 2); static final long CACHE_MOD_TIMEOUT = 3600000L * IniFile.gamma().getIniInt("NetCacheModifiedCheck", 8); static final long CACHE_MOD_TIMEDELAY = 60000L * IniFile.gamma().getIniInt("NetCacheDelay", 5); static final Date refreshStartDate = new Date(new Date().getTime() + CACHE_MOD_TIMEDELAY); static Vector threadQueue = new Vector(); public static final int START = 0; public static final int REFRESHING = 2; public static final int LOADING = 3; public static final int DONE = 4; public static final int ERROR = 5; public static final int NOSUCHFILE = 6; public static final int LOADED = 7; public transient CacheEntry next; public transient CacheEntry prev; int state; private transient int count; URL url; transient int lastRefTime; transient String encoding; String localName; transient Vector observers; public long remoteTime = 0L; public Date checkTime; public int netSize = -1; public int bytes; private transient CacheEntry nextDec; private static CacheEntry endDec = new CacheEntry(); private static CacheEntry baseDec = endDec; static int refTime = 0; static int numActiveThreads = 0; private static Object finalizeSafeLock = new Object(); private static int nextDownloader = 0; public static int getConcurrentDownloads() { return numActiveThreads; } public static synchronized void setOffline() { httpFaulted = true; stopOnFault = true; } public static boolean getOffline() { return httpFaulted; } public boolean inUse() { return this.count > 0 || this.state == 3 || this.state == 2; } private synchronized void setState(int val) { this.state = val; if (val >= 4) { this.notifyAll(); } } boolean done() { return this.state >= 4; } CacheEntry() { this.next = this; this.prev = this; } CacheEntry(URL u) { this.url = u; this.localName = Cache.assignLocalName(u); this.setState(0); } public void incRef() { synchronized (finalizeSafeLock) { this.count++; } if (this.count == 1) { this.load(); } } void safeDecRef() { synchronized (finalizeSafeLock) { if (this.nextDec == null) { this.nextDec = baseDec; baseDec = this; } else { this.count--; this.lastRefTime = refTime++; } } } synchronized void fullDecRef() { this.notifyAll(); synchronized (Cache.cache) { synchronized (finalizeSafeLock) { this.count--; this.lastRefTime = refTime++; } if (this.count == 0 && this.remoteTime <= 0L && this.state == 7) { Cache.cache.remove(this); } } } void addObserver(Observer o) { if (this.state >= 4) { o.update(null, this.url); } else { synchronized (this) { if (this.observers == null) { this.observers = new Vector(); } this.observers.addElement(o); } } } void notifyObservers() { Vector v; synchronized (this) { v = this.observers; this.observers = null; } if (v != null) { int len = v.size(); for (int i = 0; i < len; i++) { v.elementAt(i).update(null, this.url); } } } URLConnection openURL() throws Exception { int retryCount = IniFile.gamma().getIniInt("NetCacheRetries", 8); java.net.URL u = null; URLConnection uc = null; while (!httpFaulted) { try { if (u == null) { String us = this.url.unalias(); if (us.endsWith("upgrades.lst")) { us = us + "?" + (int)(Math.random() * 1000000.0); } u = DNSLookup.lookup(new java.net.URL(us)); } uc = u.openConnection(); uc.setRequestProperty("Accept-Encoding", "gzip,deflate"); if (this.remoteTime > 0L && this.bytes > 0) { uc.setIfModifiedSince(this.remoteTime); } uc.connect(); this.encoding = uc.getContentEncoding(); if (this.encoding == null) { this.encoding = new String("none"); } this.remoteTime = uc.getLastModified(); this.netSize = uc.getContentLength(); return uc; } catch (UnknownHostException var5) { if (NetUpdate.isInternalVersion() || stopOnFault) { httpFaulted = true; } throw var5; } catch (IOException var6) { retryCount--; if (wasHttpNoSuchFile(var6, uc)) { throw new FileNotFoundException("Http " + this.url); } if (retryCount <= 0 || var6 instanceof MalformedURLException) { throw var6; } if (this.state == 2) { throw var6; } System.out.println("Exception " + var6 + " opening " + this.url + ", retrying..."); } } throw new FileNotFoundException("Http " + this.url); } private static boolean wasHttpNoSuchFile(Exception e, URLConnection uc) { try { if (((HttpURLConnection)uc).getResponseCode() == 404) { return true; } } catch (Exception var3) { } return false; } public void forceRecheck() { this.checkTime = null; } public void load() { if (this.state != 2 && this.state != 3) { Date now = new Date(); if (this.state == 5 || this.state == 0) { this.setState(3); } else if (this.localName != null) { if (this.checkTime != null && now.before(refreshStartDate)) { this.notifyObservers(); return; } Date aWhileAgo = new Date(now.getTime() - CACHE_MOD_TIMEOUT); if (this.checkTime != null && this.checkTime.after(aWhileAgo) && (this.remoteTime > 0L || this.state == 6)) { this.notifyObservers(); return; } this.setState(2); } this.checkTime = now; synchronized (threadQueue) { if (numActiveThreads < MAX_THREADS) { this.startThread(); } else { threadQueue.addElement(this); } } } } private void startThread() { numActiveThreads++; Thread t = new Thread(this, "File Downloader " + ++nextDownloader); t.setDaemon(true); t.start(); } @Override public void run() { InputStream in = null; FileOutputStream out = null; URLConnection uc = null; try { try { long oldRemoteTime = this.remoteTime; int oldsize = this.netSize; int oldbytes = this.bytes; if (this.state == 2) { if (getOffline()) { this.setState(7); return; } long dirTime = DirTimeStamp.request(this.url); if (dirTime > 0L && dirTime <= oldRemoteTime && new Date().getTime() < oldRemoteTime) { this.setState(7); this.finishedLoad(); return; } uc = this.openURL(); if (this.remoteTime > 0L && this.remoteTime <= oldRemoteTime && (oldsize == -1 || this.netSize == -1 || oldsize == this.netSize || this.netSize == oldbytes)) { in = uc.getInputStream(); if (in != null) { in.close(); } this.setState(7); this.finishedLoad(); return; } if (uc instanceof HttpURLConnection) { int responseCode = ((HttpURLConnection)uc).getResponseCode(); boolean keep = false; switch (responseCode) { case 304: case 400: case 500: case 502: case 503: case 504: keep = true; } if (keep) { in = uc.getInputStream(); if (in != null) { in.close(); } this.setState(7); this.finishedLoad(); return; } } this.setState(3); } Cache.cache.totalBytes = Cache.cache.totalBytes - this.bytes; int tries; for (tries = 0; tries < 2; tries++) { this.bytes = 0; in = null; out = null; if (uc == null) { uc = this.openURL(); uc.connect(); } if (uc instanceof HttpURLConnection) { int responseCode = ((HttpURLConnection)uc).getResponseCode(); boolean keep = false; switch (responseCode) { case 304: case 500: case 502: case 503: case 504: keep = true; } if (keep) { in = uc.getInputStream(); if (in != null) { in.close(); } this.bytes = oldbytes; Cache.cache.totalBytes = Cache.cache.totalBytes + this.bytes; this.netSize = oldsize; this.setState(7); this.finishedLoad(); return; } } if (this.encoding != null && this.encoding.equalsIgnoreCase("gzip")) { in = new GZIPInputStream(uc.getInputStream()); } else if (this.encoding != null && this.encoding.equalsIgnoreCase("deflate")) { in = new InflaterInputStream(uc.getInputStream(), new Inflater(true)); } else { in = uc.getInputStream(); } Cache.cache.makeSpaceFor(this.netSize); out = new FileOutputStream(this.localName); byte[] buf = new byte[4096]; int count; while ((count = in.read(buf)) > 0) { out.write(buf, 0, count); this.bytes += count; } in.close(); in = null; out.close(); out = null; if (this.netSize == -1 || this.bytes >= this.netSize || this.encoding.equalsIgnoreCase("gzip") || this.encoding.equalsIgnoreCase("deflate")) { this.setState(7); break; } if (tries == 0) { System.out.println("Network error while downloading, trying again..."); } uc = null; } if (this.netSize >= 0 && this.bytes < this.netSize && !this.encoding.equalsIgnoreCase("gzip") && !this.encoding.equalsIgnoreCase("deflate")) { throw new InterruptedException("Network error, got " + this.bytes + " rather than " + this.netSize + " bytes."); } if (tries > 0) { System.out.println("Second download attempt succeeded."); } Cache.cache.hasChanged++; this.setState(7); } catch (Exception var17) { System.out.println("Download error for " + this.url); System.out.println("\t" + var17.getMessage()); System.out.println("\t" + var17.toString()); if (out != null) { try { out.close(); } catch (IOException var16) { } new File(this.localName).delete(); } if (wasHttpNoSuchFile(var17, uc) || var17 instanceof FileNotFoundException) { this.setState(6); this.bytes = 0; return; } else { if (this.state == 2) { Cache.cache.hasChanged++; this.setState(7); } else { this.bytes = 0; this.setState(5); } return; } } } finally { Cache.cache.totalBytes = Cache.cache.totalBytes + this.bytes; if (this.netSize <= 0) { this.netSize = this.bytes; } this.finishedLoad(); } } private void finishedLoad() { this.notifyObservers(); synchronized (threadQueue) { numActiveThreads--; while (numActiveThreads < MAX_THREADS && !threadQueue.isEmpty()) { CacheEntry eNext = threadQueue.elementAt(0); threadQueue.removeElementAt(0); eNext.startThread(); } } if (numActiveThreads <= 0 && threadQueue.isEmpty()) { Cache.cache.resyncIndex(); } } }