001: package org.drools.agent;
002:
003: import java.io.File;
004: import java.io.FileOutputStream;
005: import java.io.IOException;
006: import java.io.ObjectOutputStream;
007: import java.io.UnsupportedEncodingException;
008: import java.net.MalformedURLException;
009: import java.net.URL;
010: import java.net.URLEncoder;
011: import java.util.ArrayList;
012: import java.util.HashMap;
013: import java.util.List;
014: import java.util.Map;
015: import java.util.Properties;
016:
017: import org.drools.RuntimeDroolsException;
018: import org.drools.rule.Package;
019:
020: public class URLScanner extends PackageProvider {
021:
022: //this is the URLs we are managing
023: URL[] urls;
024:
025: //this is only set if we are using a local cache - only fall back on this
026: //when URL connection is not available.
027: FileScanner localCacheFileScanner;
028:
029: //this is used to access the remote resources
030: IHttpClient httpClient = new HttpClientImpl();
031:
032: //a record of the last updated URL timestamps
033: Map lastUpdated = new HashMap();
034:
035: File localCacheDir;
036:
037: void configure(Properties config) {
038: List uriList = RuleAgent.list(config
039: .getProperty(RuleAgent.URLS));
040: urls = new URL[uriList.size()];
041: for (int i = 0; i < uriList.size(); i++) {
042: String url = (String) uriList.get(i);
043: try {
044: urls[i] = new URL(url);
045: } catch (MalformedURLException e) {
046: throw new RuntimeException("The URL " + url
047: + " is not valid.", e);
048: }
049: }
050:
051: //if we have a local cache, check its all kosher
052: String localCache = config
053: .getProperty(RuleAgent.LOCAL_URL_CACHE);
054: if (localCache != null) {
055: localCacheDir = new File(localCache);
056: if (!localCacheDir.isDirectory()) {
057: throw new RuntimeDroolsException("The local cache dir "
058: + localCache + " is a file, not a directory.");
059: }
060: this .localCacheFileScanner = new FileScanner();
061: this .localCacheFileScanner.setFiles(getFiles(urls,
062: localCacheDir));
063: }
064: }
065:
066: File[] getFiles(URL[] urls, File cacheDir) {
067: File[] fs = new File[urls.length];
068: for (int i = 0; i < urls.length; i++) {
069: URL u = urls[i];
070: File f = getLocalCacheFileForURL(cacheDir, u);
071: fs[i] = f;
072: }
073: return fs;
074: }
075:
076: static File getLocalCacheFileForURL(File cacheDir, URL u) {
077: File f;
078: try {
079: f = new File(cacheDir, URLEncoder.encode(
080: u.toExternalForm(), "UTF-8"));
081: } catch (UnsupportedEncodingException e) {
082: throw new RuntimeDroolsException(e);
083: }
084: return f;
085: }
086:
087: Package[] loadPackageChanges() { //void updateRuleBase(RuleBase rb, boolean removeExistingPackages) {
088: Package[] changes = null;
089: try {
090: changes = getChangeSet();
091: return changes;
092: } catch (IOException e) {
093: if (this .localCacheFileScanner != null) {
094: listener.warning("Falling back to local cache.");
095: return localCacheFileScanner.loadPackageChanges();
096: }
097: listener.exception(e);
098: } catch (ClassNotFoundException e) {
099: this .listener.exception(e);
100: this .listener
101: .warning("Was unable to load a class when loading a package. Perhaps it is missing from this application.");
102: }
103: return null;
104: }
105:
106: private Package[] getChangeSet() throws IOException,
107: ClassNotFoundException {
108: if (this .urls == null)
109: return new Package[0];
110: List list = new ArrayList();
111: for (int i = 0; i < urls.length; i++) {
112: URL u = urls[i];
113: if (hasChanged(u, this .lastUpdated)) {
114: Package p = readPackage(u);
115: if (p == null)
116: return null;
117: list.add(p);
118: if (localCacheDir != null) {
119: writeLocalCacheCopy(p, u, localCacheDir);
120: }
121: }
122: }
123: return (Package[]) list.toArray(new Package[list.size()]);
124: }
125:
126: private void writeLocalCacheCopy(Package p, URL u,
127: File localCacheDir) {
128: File local = getLocalCacheFileForURL(localCacheDir, u);
129: if (local.exists())
130: local.delete();
131:
132: try {
133: ObjectOutputStream out = new ObjectOutputStream(
134: new FileOutputStream(local));
135: out.writeObject(p);
136: out.flush();
137: out.close();
138: } catch (IOException e) {
139: listener.exception(e);
140: listener
141: .warning("Was an error with the local cache directory "
142: + localCacheDir.getPath());
143: }
144:
145: }
146:
147: private Package readPackage(URL u) throws IOException,
148: ClassNotFoundException {
149: return httpClient.fetchPackage(u);
150: }
151:
152: private boolean hasChanged(URL u, Map updates) throws IOException {
153: LastUpdatedPing pong = httpClient.checkLastUpdated(u);
154: if (pong.isError()) {
155: listener.warning("Was an error contacting "
156: + u.toExternalForm() + ". Reponse header: "
157: + pong.responseMessage);
158: throw new IOException("Was unable to reach server.");
159: }
160:
161: String url = u.toExternalForm();
162: if (!updates.containsKey(url)) {
163: updates.put(url, new Long(pong.lastUpdated));
164: return true;
165: } else {
166: Long last = (Long) updates.get(url);
167: if (last.longValue() < pong.lastUpdated) {
168: updates.put(url, new Long(pong.lastUpdated));
169: return true;
170: } else {
171: return false;
172: }
173: }
174: }
175:
176: public String toString() {
177: String s = "URLScanner monitoring URLs: ";
178: if (this .urls != null) {
179: for (int i = 0; i < urls.length; i++) {
180: URL url = urls[i];
181: s = s + " " + url.toExternalForm();
182: }
183: }
184: if (this .localCacheDir != null) {
185: s = s + " with local cache dir of "
186: + this.localCacheDir.getPath();
187: }
188: return s;
189: }
190:
191: }
|