package org.drools.agent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.drools.RuntimeDroolsException;
import org.drools.common.DroolsObjectOutputStream;
import org.drools.rule.Package;
public class URLScanner extends PackageProvider {
//this is the URLs we are managing
URL[] urls;
//this is only set if we are using a local cache - only fall back on this
//when URL connection is not available.
FileScanner localCacheFileScanner;
//this is used to access the remote resources
IHttpClient httpClient = new HttpClientImpl();
//a record of the last updated URL timestamps
Map lastUpdated = new HashMap();
File localCacheDir;
void configure(Properties config) {
List uriList = RuleAgent.list( config.getProperty( RuleAgent.URLS ) );
urls = new URL[uriList.size()];
for ( int i = 0; i < uriList.size(); i++ ) {
String url = (String) uriList.get( i );
try {
urls[i] = new URL( url );
} catch ( MalformedURLException e ) {
throw new RuntimeException( "The URL " + url + " is not valid.",
e );
}
}
//if we have a local cache, check its all kosher
String localCache = config.getProperty( RuleAgent.LOCAL_URL_CACHE );
if ( localCache != null ) {
localCacheDir = new File( localCache );
if ( !localCacheDir.isDirectory() ) {
throw new RuntimeDroolsException( "The local cache dir " + localCache + " is a file, not a directory." );
}
this.localCacheFileScanner = new FileScanner();
this.localCacheFileScanner.setFiles( getFiles( urls, localCacheDir ) );
this.localCacheFileScanner.setAgentListener( this.listener );
}
}
public void setAgentListener(AgentEventListener listener) {
super.setAgentListener( listener );
if ( this.localCacheFileScanner != null ) {
this.localCacheFileScanner.setAgentListener( listener );
}
}
File[] getFiles(URL[] urls, File cacheDir) {
File[] fs = new File[urls.length];
for ( int i = 0; i < urls.length; i++ ) {
URL u = urls[i];
File f = getLocalCacheFileForURL( cacheDir, u );
fs[i] = f;
}
return fs;
}
static File getLocalCacheFileForURL(File cacheDir, URL u) {
File f;
try {
f = new File( cacheDir,
URLEncoder.encode( u.toExternalForm(), "UTF-8" ) + ".pkg" );
} catch ( UnsupportedEncodingException e ) {
throw new RuntimeDroolsException( e );
}
return f;
}
PackageChangeInfo loadPackageChanges() { //void updateRuleBase(RuleBase rb, boolean removeExistingPackages) {
PackageChangeInfo changes = null;
try {
changes = getChangeSet();
return changes;
} catch ( IOException e ) {
listener.exception( e );
if (this.localCacheFileScanner != null) {
listener.warning( "Falling back to local cache." );
return localCacheFileScanner.loadPackageChanges();
}
} catch ( ClassNotFoundException e ) {
this.listener.exception( e );
this.listener.warning( "Was unable to load a class when loading a package. Perhaps it is missing from this application." );
}
return null;
}
private PackageChangeInfo getChangeSet() throws IOException, ClassNotFoundException {
PackageChangeInfo info = new PackageChangeInfo();
if ( this.urls == null ) return info;
for ( int i = 0; i < urls.length; i++ ) {
URL u = urls[i];
if ( hasChanged( u, this.lastUpdated) ) {
Package p = readPackage( u );
if ( p == null ) return null;
info.addPackage( p );
if (localCacheDir != null) {
writeLocalCacheCopy(p, u, localCacheDir);
}
}
}
return info;
}
private void writeLocalCacheCopy(Package p, URL u, File localCacheDir) {
File local = getLocalCacheFileForURL( localCacheDir, u );
if (local.exists()) local.delete();
try {
ObjectOutput out = new DroolsObjectOutputStream(new FileOutputStream(local));
out.writeObject( p );
out.flush();
out.close();
} catch (IOException e) {
listener.exception( e );
listener.warning( "Was an error with the local cache directory " + localCacheDir.getPath() );
}
}
private Package readPackage(URL u) throws IOException, ClassNotFoundException {
return httpClient.fetchPackage( u );
}
private boolean hasChanged(URL u, Map updates) throws IOException {
LastUpdatedPing pong = httpClient.checkLastUpdated( u );
if (pong.isError()) {
listener.warning( "Was an error contacting " + u.toExternalForm() + ". Reponse header: " + pong.responseMessage );
throw new IOException("Was unable to reach server.");
}
String url = u.toExternalForm();
if ( !updates.containsKey( url ) ) {
updates.put( url,
new Long( pong.lastUpdated ) );
return true;
} else {
Long last = (Long) updates.get( url );
if ( last.longValue() < pong.lastUpdated ) {
updates.put( url,
new Long( pong.lastUpdated ) );
return true;
} else {
return false;
}
}
}
public String toString() {
String s = "URLScanner monitoring URLs: ";
if (this.urls != null) {
for ( int i = 0; i < urls.length; i++ ) {
URL url = urls[i];
s = s + " " + url.toExternalForm();
}
}
if (this.localCacheDir != null) {
s = s + " with local cache dir of " + this.localCacheDir.getPath();
}
return s;
}
}