// Copyright (c) 2001 Dustin Sallings <dustin@spy.net> package net.spy.net; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import net.spy.SpyObject; import net.spy.concurrent.SynchronizationObject; /** * A particular URL that's being watched. */ public class URLItem extends SpyObject implements Runnable { private static final int DEFAULT_UPDATE_FREQ = 1800000; // How long a URL will be watched if nobody wants it (defaults to a // half hour). private int maxIdleTime=1800000; private int updateFrequency=DEFAULT_UPDATE_FREQ; private long lastRequest=0; private int numUpdates=0; private URL url=null; private Map<String, List<String>> lastHeaders=null; private SynchronizationObject<String> content=null; private long lastModified=0; private boolean isRunning=true; private IOException lastError=null; /** * Get an instance of URLItem. * * @param u URL to watch */ public URLItem(URL u) { this(u, DEFAULT_UPDATE_FREQ); } /** * Get an instance of URLItem. * * @param u URL to watch * @param i the update frequency */ public URLItem(URL u, int i) { super(); this.updateFrequency=i; this.url=u; lastRequest=System.currentTimeMillis(); content=new SynchronizationObject<String>(null); } /** * Get a fetcher (override for testing). */ protected HTTPFetch getFetcher(Map<String, List<String>> headers) { return(new HTTPFetch(url, headers)); } private void setContent(String to, Map<String, List<String>> headers, long lastMod) { content.set(to); // Big chunk of debug logging. if(getLogger().isDebugEnabled()) { String c=content.get(); getLogger().debug("Setting content for %s: %s", this, c==null?"<null>":c.length() + " bytes"); } lastModified=lastMod; lastHeaders=headers; } /** * Get the content from the last fetch. */ public String getContent() throws IOException { lastRequest=System.currentTimeMillis(); if(lastError!=null) { throw lastError; } String c=content.get(); if(getLogger().isDebugEnabled()) { getLogger().debug("Getting content for %s: %s", this, c==null?"<null>":c.length() + " bytes"); } return(c); } /** * Wait for content to arrive. */ public String waitForContent(long dur, TimeUnit timeunit) throws InterruptedException, TimeoutException { content.waitUntilNotNull(dur, timeunit); return content.get(); } /** * Find out when the last request was. * * @return the timestamp of the last request. */ public long getLastRequest() { return(lastRequest); } /** * Get the URL this thing is watching. * * @return the URL */ public URL getURL() { return(url); } /** * Set the maximum number of milliseconds this URL will remain in the * container if nothing requests it. */ public void setMaxIdleTime(int to) { this.maxIdleTime=to; } /** * Get the maximum number of milliseconds this URL will remain in the * container if nothing requests it. */ public int getMaxIdleTime() { return(maxIdleTime); } /** * True if this is still running. */ public boolean isRunning() { return isRunning; } /** * Get the update frequency. */ public int getUpdateFrequency() { return updateFrequency; } public void run() { HashMap<String, List<String>> headers=new HashMap<String, List<String>>(); // make sure the stuff isn't cached ArrayList<String> tmp=new ArrayList<String>(); tmp.add("no-cache"); headers.put("Pragma", tmp); // But don't request something if we know we already have it. if(lastHeaders != null) { List<String> eTags=lastHeaders.get("ETag"); if(eTags != null) { // Put the etags in the none-match headers.put("If-None-Match", eTags); } } numUpdates++; try { getLogger().info("Updating %s", url); HTTPFetch hf=getFetcher(headers); hf.setIfModifiedSince(lastModified); if(hf.getStatus() == HttpURLConnection.HTTP_OK) { getLogger().info("Updated %s", url); setContent(hf.getData(), hf.getResponseHeaders(), hf.getLastModified()); } else { getLogger().info("Not saving content due to response status %s", hf.getStatus()); } } catch(IOException e) { lastError=e; } if((System.currentTimeMillis() - lastRequest) > maxIdleTime) { isRunning=false; getLogger().info("Stopped updating %s", url); } } }