package de.axone.tools.watcher; import java.io.IOException; import java.io.Serializable; import java.net.URISyntaxException; import java.util.HashMap; import org.apache.http.client.ClientProtocolException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.axone.tools.HttpUtil; import de.axone.tools.HttpUtil.HttpUtilResponse; import de.axone.web.SuperURL; /** * Tells if a web resource needs reloading. * * This is done by means of e-tag/expiry watching and some kind of minimal recheck * timeout to minimize worst case network load. * * Due to the nature of http this class returns the access to the changed * file if it has changed. (Request if-changed) * * This class is similar in use to FileWatcher * * @see de.axone.tools.watcher.FileWatcher * * @author flo */ public class HttpWatcher implements Serializable { private static final long serialVersionUID = 1L; public static final Logger log = LoggerFactory.getLogger( HttpWatcher.class ); private static final long SECONDS = 1000; private static final long DAYS = SECONDS * 60 * 60 * 24; private static final long TIMEOUT = 2 * SECONDS; private static final long MAX_TIMEOUT = 1 * DAYS; // At least recheck once a day //private static final long MAX_TIMEOUT = 1 * SECONDS; // For manual testing private final SuperURL url; private final long minTimeout; private String eTag; private String lastModified; private long expires = -1; private long lastCheckTime = -1; private long timeout = -1; /** * Create a HttpWatcher for one url with the given * timeout. * * @param url to watch * @param minTimeout which has to pass until a new check is done */ public HttpWatcher( SuperURL url, long minTimeout ){ this.url = url; this.minTimeout = minTimeout; } /** * Create a HttpWatcher with a default timeout of 2s * * @param url to watch */ public HttpWatcher( SuperURL url ){ this( url, TIMEOUT ); } public HttpUtilResponse hasChanged(){ long time = System.currentTimeMillis(); long div = time - lastCheckTime; HttpUtilResponse result = null; // See if timeout has passed to do a recheck if( div >= timeout || timeout < 0 ){ lastCheckTime = time; try { if( log.isTraceEnabled() ) log.trace( "Check for: " + url + "(" + eTag + ")" ); HttpUtilResponse response = HttpUtil.request( url.toURL(), eTag, lastModified ); log.trace( response.toString() ); if( response.code == 200 ){ eTag = response.eTag; lastModified = response.lastModified; long maxAgeMs = response.maxAge * 1000; maxAgeMs = ( maxAgeMs > minTimeout ? maxAgeMs : minTimeout ); maxAgeMs = ( maxAgeMs < MAX_TIMEOUT ? maxAgeMs : MAX_TIMEOUT ); timeout = maxAgeMs; result = response; } } catch( ClientProtocolException e ) { log.error( url.toDebug(), e ); } catch( URISyntaxException e ) { log.error( url.toDebug(), e ); } catch( IOException e ) { log.error( url.toDebug(), e ); } } return result; } public SuperURL getURL(){ return url; } @Override public String toString(){ return "FileWatcher for: " + url + " timeout: " + timeout + " E-Tag: " + eTag + " Expires: " + expires + " Last check: + " + lastCheckTime; } /* STATIC METHODS for persistent Watchers */ private static HashMap<String, HttpWatcher> staticWatchers = new HashMap<String, HttpWatcher>(); public synchronized static HttpWatcher staticWatcher( SuperURL url, long timeout ){ String key = url.toUnique() + "/" + timeout; if( ! staticWatchers.containsKey( key ) ){ staticWatchers.put( key, new HttpWatcher( url, timeout ) ); } return staticWatchers.get( key ); } public static HttpWatcher staticWatcher( SuperURL url ){ return staticWatcher( url, TIMEOUT ); } }