package org.jconfig;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.jconfig.event.FileListener;
import org.jconfig.event.FileListenerEvent;
/**
* A class which implements an event dispatching mechanism to all
* classes supporting the FileListener interface. This class will
* notify all FileListeners when the configuration changes. Once
* the URLFileWatcher has been shutdown, the class needs to be
* reinstanciated and restarted.
*
* @author Steve Braun <steve.braun@cogeco.ca>
* @see org.jconfig.FileWatcher
*/
public class URLFileWatcher extends Thread {
private List fileListenerList;
private volatile Thread watcher;
private int interval = 10000;
private long lastmodified;
private java.net.URL configURL = null;
private String url;
//private InputStream is = null;
private URLConnection con = null;
/**
* Constructs a FileWatcher watching the specified File
*
* @param url The File to be watched
*/
public URLFileWatcher( String url ) throws FileWatcherException
{
if( url == null ) {
throw new NullPointerException("URL cannot be <null>");
}
this.url = url;
try
{
ClassLoader cl = this.getClass().getClassLoader();
InputStream jcf = cl.getResourceAsStream( "jconfig.properties" );
// it is possible that the jconfig.properties does not exist, we get null
if ( jcf != null )
{
Properties jcfProperties = new Properties();
jcfProperties.load( jcf );
// load what is set in system
Properties prop = System.getProperties();
// if we see http.proxyHost and/or http.proxyPort inside
// the jconfig.properties, we can set the System.properties
// for use by the URLConnection object
if ( jcfProperties.getProperty( "http.proxyHost" ) != null )
prop.put( "http.proxyHost", jcfProperties.getProperty( "http.proxyHost" ) );
if ( jcfProperties.getProperty( "http.proxyPort" ) != null )
prop.put( "http.proxyPort", jcfProperties.getProperty( "http.proxyPort" ) );
if ( jcfProperties.getProperty( "watcher.interval" ) != null )
{
try
{
setInterval( Integer.parseInt( jcfProperties.getProperty( "watcher.interval" ) ) );
}
catch ( NumberFormatException nfe )
{
throw new FileWatcherException( nfe.getMessage() );
}
}
}
}
catch ( IOException ioe )
{
throw new FileWatcherException( ioe.getMessage() );
}
try
{
this.configURL = new URL( url );
this.con = this.configURL.openConnection();
this.lastmodified = con.getLastModified();
this.fileListenerList = new Vector();
}
catch ( MalformedURLException m )
{
throw new FileWatcherException( m.getMessage() );
}
catch ( IOException ioe )
{
throw new FileWatcherException( ioe.getMessage() );
}
}
/**
* Adds FileListener
*
* @param fileListener The FileListener
*/
public void addFileListener( FileListener fileListener ) {
fileListenerList.add( fileListener );
}
/**
* Set the timer interval. The default is 10 seconds
*
* @param seconds The number of seconds to set the interval when
* to check for the changes to the file.
*/
public void setInterval( int seconds ) {
this.interval = seconds*1000;
}
/**
* Tell thread to stop watching. Currently once a Thread is started
* and stopped, a new FileWatcher will be required.
*/
public void stopWatching() {
this.watcher = null;
}
/**
* Start the Thread on its journey to scan for changes to the
* file it is watching.
*/
public void start() {
watcher = new Thread( this );
watcher.setDaemon( true );
watcher.start();
}
/**
* Start the thread to call checkFile()
*/
public void run()
{
Thread thisThread = Thread.currentThread();
while( thisThread == watcher )
{
try
{
Thread.sleep( interval );
}
catch ( InterruptedException e )
{
// can't do much from here with Exception
watcher = null;
}
try
{
checkURLFile();
}
catch( Exception e )
{
//e.printStackTrace();
// can't do much from here with Exception
watcher = null;
}
}
}
/**
* Retrieve an array of FileListeners.
*
* @return FileListeners as array of FileListener
*/
public FileListener[] getFileListeners()
{
return ( FileListener[] ) fileListenerList.toArray();
}
/* allows us to update the File object, in case we need to. */
/**
* Sets a new File to be watched. This causes the FileWatcher to watch
* the given File and disregard the File that was used during Construction.
*
* @param url The File to be watched
*/
public void setURL( String url ) throws FileWatcherException
{
if ( url == null ) throw new NullPointerException( "url cannot be <null>" );
this.url = url;
try
{
this.configURL = new URL( url );
}
catch ( MalformedURLException e )
{
throw new FileWatcherException( e.getMessage() );
}
}
public String getURLString()
{
return ( url );
}
public URL getURL()
{
return ( configURL );
}
public void setURL( URL configURL )
{
if ( configURL == null ) throw new NullPointerException( "URL cannot be <null>" );
this.configURL = configURL;
}
/* looks at the internal FileListenerList and keeps track of changed info */
private void checkURLFile() throws FileWatcherException
{
try
{
URL u = configURL;
URLConnection uConn = u.openConnection();
long mod = uConn.getLastModified();
if( mod > lastmodified )
{
lastmodified = mod;
Iterator iterator = fileListenerList.iterator();
while( iterator.hasNext() )
{
FileListener listener = (FileListener)iterator.next();
//listener.fileChanged( new FileListenerEvent( newFile ) );
listener.fileChanged( new FileListenerEvent( null ) );
}
}
}
catch ( IOException ioe )
{
throw new FileWatcherException( ioe.getMessage() );
}
}
}