[jboss-svn-commits] JBL Code SVN: r28588 - in labs/jbossrules/trunk: drools-compiler/src/test/java/org/drools/agent and 1 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Wed Jul 29 22:40:58 EDT 2009


Author: michael.neale at jboss.com
Date: 2009-07-29 22:40:58 -0400 (Wed, 29 Jul 2009)
New Revision: 28588

Added:
   labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/UrlResourceTest.java
Modified:
   labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentFactory.java
   labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentTest.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/UrlResource.java
Log:
JBRULES-2219 UrlResource caching

Modified: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentFactory.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentFactory.java	2009-07-30 01:33:01 UTC (rev 28587)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentFactory.java	2009-07-30 02:40:58 UTC (rev 28588)
@@ -74,6 +74,17 @@
  * ResourceFactory.getResourceChangeNotifierService().start();
  * ResourceFactory.getResourceChangeScannerService().start();
  * </pre>
+ *
+ * <p>
+ * For resources that are "polled" from a remote source (via http or similar) - sometimes you may want a local file based cache,
+ * in case the remote service is not available for whatever reason.
+ * To enable this:
+ * Set the system property: "drools.resource.urlcache" to a directory which can be written to and read from
+ * as a cache - so remote resources will be cached with last known good copies. This will allow the service to be restarted
+ * even if the remote source is not available. 
+ * For example -Ddrools.resource.urlcache=/home/rulecaches
+ *
+ * </p>
  * 
  * @see org.drools.agent.KnowledgeAgent
  * @see org.drools.agent.KnowledgeAgentConfiguration

Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentTest.java	2009-07-30 01:33:01 UTC (rev 28587)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentTest.java	2009-07-30 02:40:58 UTC (rev 28588)
@@ -33,6 +33,7 @@
 import org.drools.io.ResourceFactory;
 import org.drools.io.impl.ResourceChangeNotifierImpl;
 import org.drools.io.impl.ResourceChangeScannerImpl;
+import org.drools.io.impl.UrlResource;
 import org.drools.runtime.StatefulKnowledgeSession;
 import org.drools.runtime.StatelessKnowledgeSession;
 import org.drools.runtime.pipeline.Action;
@@ -80,6 +81,10 @@
     }
     
     public void  testModifyFileUrl() throws Exception {
+
+
+        UrlResource.CACHE_DIR = new File(".");
+
         String rule1 = "";
         rule1 += "package org.drools.test\n";
         rule1 += "global java.util.List list\n";
@@ -184,7 +189,24 @@
 
         assertTrue( list.contains( "rule3" ) );
         assertTrue( list.contains( "rule2" ) );
-        kagent.monitorResourceChangeEvents( false );        
+        kagent.monitorResourceChangeEvents( false );
+
+        server.stop();
+
+
+        //now try it with the server stopped, so cache has to be used...
+        kagent = KnowledgeAgentFactory.newKnowledgeAgent( "test agent",
+                                                                         kbase,
+                                                                         aconf );
+        kagent.monitorResourceChangeEvents(true);
+        assertEquals("test agent", kagent.getName());
+
+
+        //hmmm...
+        kagent.applyChangeSet( ResourceFactory.newFileResource(fxml)  );
+
+        ksession = kagent.getKnowledgeBase().newStatefulKnowledgeSession();
+        assertNotNull(ksession);
     }
     
     public void  testModifyFileUrlWithStateless() throws Exception {
@@ -286,7 +308,12 @@
         
         assertTrue( list.contains( "rule3" ) );
         assertTrue( list.contains( "rule2" ) );
-        kagent.monitorResourceChangeEvents( false );        
+
+
+        kagent.monitorResourceChangeEvents( false );
+
+
+
     }    
 
     public void  testModifyPackageUrl() throws Exception {

Added: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/UrlResourceTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/UrlResourceTest.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/UrlResourceTest.java	2009-07-30 02:40:58 UTC (rev 28588)
@@ -0,0 +1,162 @@
+package org.drools.agent;
+
+import junit.framework.TestCase;
+import org.drools.io.impl.UrlResource;
+import org.drools.io.impl.ResourceChangeScannerImpl;
+import org.drools.io.impl.ResourceChangeNotifierImpl;
+import org.drools.io.ResourceFactory;
+import org.drools.util.FileManager;
+import org.drools.util.StringUtils;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.ResourceHandler;
+
+import java.io.File;
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+/**
+ * @author Michael Neale
+ */
+public class UrlResourceTest extends TestCase {
+    private FileManager fileManager;
+    private Server server;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        fileManager = new FileManager();
+        fileManager.setUp();
+        ((ResourceChangeScannerImpl) ResourceFactory.getResourceChangeScannerService()).reset();
+        ResourceFactory.getResourceChangeNotifierService().start();
+        ResourceFactory.getResourceChangeScannerService().start();
+
+        this.server = new Server( 9000 );
+        ResourceHandler resourceHandler = new ResourceHandler();
+        resourceHandler.setResourceBase( fileManager.getRootDirectory().getPath() );
+        System.out.println("root : " + fileManager.getRootDirectory().getPath() );
+
+        server.setHandler( resourceHandler );
+
+        server.start();
+    }
+
+    protected void tearDown() throws Exception {
+        fileManager.tearDown();
+        ResourceFactory.getResourceChangeNotifierService().stop();
+        ResourceFactory.getResourceChangeScannerService().stop();
+        ((ResourceChangeNotifierImpl) ResourceFactory.getResourceChangeNotifierService()).reset();
+        ((ResourceChangeScannerImpl) ResourceFactory.getResourceChangeScannerService()).reset();
+
+        server.stop();
+    }
+
+
+    public void testWithCache() throws Exception {
+        URL url = new URL("http://localhost:9000/rule1.drl");
+        UrlResource ur = new UrlResource(url);
+        UrlResource.CACHE_DIR = new File(".");
+
+        File f1 = fileManager.newFile( "rule1.drl" );
+        System.err.println("target file: " + f1.getAbsolutePath());
+        Writer output = new BufferedWriter( new FileWriter( f1 ) );
+        output.write( "Some data" );
+        output.close();
+
+        long lm = ur.getLastModified();
+        assertTrue(lm > 0);
+
+        InputStream ins = ur.getInputStream();
+        assertNotNull(ins);
+
+        server.stop();
+
+        assertNotNull(ur.getInputStream());
+
+        assertTrue(ur.getLastModified() > 0);
+
+        assertTrue(ur.getInputStream() instanceof FileInputStream);
+
+
+        //now write some more stuff
+        Thread.sleep(1000);
+        f1.delete();
+        output = new BufferedWriter( new FileWriter( f1 ) );
+        output.write( "More data..." );
+        output.close();
+
+        server.start();
+        assertNotNull(ur.getInputStream());
+        assertFalse(ur.getInputStream() instanceof FileInputStream);
+        long lm_ = ur.getLastModified();
+        System.err.println("lm_ : " + lm_ + " lm : " + lm );
+
+        assertTrue(lm_ > lm);
+
+        InputStream in_= ur.getInputStream();
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(in_));
+        String line = rdr.readLine();
+        assertEquals("More data...", line);
+
+        server.stop();
+
+        Thread.sleep(1000);
+        f1.delete();
+        output = new BufferedWriter( new FileWriter( f1 ) );
+        output.write( "Finally.." );
+        output.close();
+
+        //now it should be cached, so using old copy still... (server has stopped serving it up)
+        ur = new UrlResource(url);
+        in_= ur.getInputStream();
+        rdr = new BufferedReader(new InputStreamReader(in_));
+        line = rdr.readLine();
+        assertEquals("More data...", line);
+
+        Thread.sleep(1000);
+        server.start();
+
+        ur = new UrlResource(url);
+        //server is started, so should have latest...
+        in_= ur.getInputStream();
+        rdr = new BufferedReader(new InputStreamReader(in_));
+        line = rdr.readLine();
+        assertEquals("Finally..", line);
+
+
+
+
+    }
+
+    public void testWithoutCache() throws Exception {
+        UrlResource ur = new UrlResource(new URL("http://localhost:9000/rule1.drl"));
+        UrlResource.CACHE_DIR = null;
+
+        File f1 = fileManager.newFile( "rule1.drl" );
+        System.err.println("target file: " + f1.getAbsolutePath());
+        Writer output = new BufferedWriter( new FileWriter( f1 ) );
+        output.write( "Some data" );
+        output.close();
+
+        long lm = ur.getLastModified();
+        assertTrue(lm > 0);
+
+        InputStream ins = ur.getInputStream();
+        assertNotNull(ins);
+
+        server.stop();
+        assertEquals(0, ur.getLastModified());
+
+
+
+
+
+    }
+
+
+    
+}


Property changes on: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/UrlResourceTest.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/UrlResource.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/UrlResource.java	2009-07-30 01:33:01 UTC (rev 28587)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/UrlResource.java	2009-07-30 02:40:58 UTC (rev 28588)
@@ -8,10 +8,16 @@
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -25,13 +31,23 @@
 /**
  * Borrowed gratuitously from Spring under ASL2.0.
  *
+ * Added in local file cache ability for http and https urls.
+ *
+ * Set the system property: "drools.resource.urlcache" to a directory which can be written to and read from
+ * as a cache - so remote resources will be cached with last known good copies.
  */
 public class UrlResource extends BaseResource
     implements
     InternalResource,
     Externalizable {
+
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    public static File CACHE_DIR = getCacheDir();
+
     private URL  url;
     private long lastRead = -1;
+    private static final String DROOLS_RESOURCE_URLCACHE = "drools.resource.urlcache";
 
     public UrlResource() {
 
@@ -70,7 +86,69 @@
      * @see java.net.URLConnection#getInputStream()
      */
     public InputStream getInputStream() throws IOException {
-        this.lastRead = getLastModified();
+        try {
+            long lastMod  = grabLastMod();
+            if (lastMod == 0) {
+                //we will try the cache...
+                if (cacheFileExists()) return fromCache();
+            }
+            if (lastMod > 0 && lastMod > lastRead) {
+                if (CACHE_DIR != null && url.getProtocol().equals("http") || url.getProtocol().equals("https")) {
+                    //lets grab a copy and cache it in case we need it in future...
+                    cacheStream();
+                }
+            }
+            this.lastRead = lastMod;
+            return grabStream();
+        } catch (IOException e) {
+            if (cacheFileExists()) {
+                return fromCache();
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private boolean cacheFileExists() {
+        return CACHE_DIR != null && getCacheFile().exists();
+    }
+
+    private InputStream fromCache() throws FileNotFoundException, UnsupportedEncodingException {
+       File fi = getCacheFile();
+       return new FileInputStream(fi);
+    }
+
+    private File getCacheFile()  {
+        try {
+            return new File(CACHE_DIR, URLEncoder.encode(this.url.toString(), "UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Save a copy in the local cache - in case remote source is not available in future.
+     */
+    private void cacheStream() {
+        try {
+            File fi = getCacheFile();
+            if (fi.exists()) fi.delete();
+            FileOutputStream fout = new FileOutputStream(fi);
+            InputStream in = grabStream();
+            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+            int n;
+            while (-1 != (n = in.read(buffer))) {
+                fout.write(buffer, 0, n);
+            }
+            fout.flush();
+            fout.close();
+            in.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private InputStream grabStream() throws IOException {
         URLConnection con = this.url.openConnection();
         con.setUseCaches( false );
         return con.getInputStream();
@@ -85,7 +163,6 @@
      * @param originalUrl the original URL
      * @param originalPath the original URL path
      * @return the cleaned URL
-     * @see org.springframework.util.StringUtils#cleanPath
      */
     private URL getCleanedUrl(URL originalUrl,
                               String originalPath) {
@@ -116,30 +193,46 @@
 
     public long getLastModified() {
         try {
-            // use File, as http rounds milliseconds on some machines, this fine level of granularity is only really an issue for testing
-            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504473
-            if ( "file".equals( url.getProtocol() ) ) {
-                File file = getFile();
-                return file.lastModified();
+            long lm = grabLastMod();
+            //try the cache.
+            if (lm == 0 && cacheFileExists()) {
+                //OK we will return it from the local cached copy, as remote one isn't available.. 
+                return getCacheFile().lastModified();
+            }
+            return lm;
+        } catch ( IOException e ) {
+            //try the cache...
+            if (cacheFileExists()) {
+                //OK we will return it from the local cached copy, as remote one isn't available..
+                return getCacheFile().lastModified();
             } else {
-                URLConnection conn = getURL().openConnection();
-                if ( conn instanceof HttpURLConnection ) {
-                    ((HttpURLConnection) conn).setRequestMethod( "HEAD" );
-                }
-                long date =  conn.getLastModified();
-                if (date == 0) {
-                     try {
-                         date = Long.parseLong(conn.getHeaderField("lastModified"));
-                     } catch (Exception e) { /* well, we tried ... */ }
-                }
-                return date;
+                throw new RuntimeException( "Unable to get LastMofified for ClasspathResource",
+                                            e );
             }
-        } catch ( IOException e ) {
-            throw new RuntimeException( "Unable to get LastMofified for ClasspathResource",
-                                        e );
         }
     }
 
+    private long grabLastMod() throws IOException {
+        // use File, as http rounds milliseconds on some machines, this fine level of granularity is only really an issue for testing
+        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504473
+        if ( "file".equals( url.getProtocol() ) ) {
+            File file = getFile();
+            return file.lastModified();
+        } else {
+            URLConnection conn = getURL().openConnection();
+            if ( conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).setRequestMethod( "HEAD" );
+            }
+            long date =  conn.getLastModified();
+            if (date == 0) {
+                 try {
+                     date = Long.parseLong(conn.getHeaderField("lastModified"));
+                 } catch (Exception e) { /* well, we tried ... */ }
+            }
+            return date;
+        }
+    }
+
     public long getLastRead() {
         return this.lastRead;
     }
@@ -203,4 +296,12 @@
         return "[UrlResource path='" + this.url.toString() + "']";
     }
 
+    private static File getCacheDir() {
+        String root = System.getProperty(DROOLS_RESOURCE_URLCACHE, "NONE");
+        if (root.equals("NONE")) {
+            return null;
+        } else {
+            return new File(root);
+        }
+    }
 }



More information about the jboss-svn-commits mailing list