Author: michiel
Date: 2009-05-13 22:40:21 +0200 (Wed, 13 May 2009)
New Revision: 35145

Modified:
   mmbase/trunk/src/org/mmbase/framework/CachedRenderer.java
   mmbase/trunk/src/org/mmbase/framework/DocumentationRenderer.java
   mmbase/trunk/src/org/mmbase/framework/Utils.java
   mmbase/trunk/src/org/mmbase/framework/basic/State.java
Log:
Rendering documention can take very long. Added some stuff to the 
CachedRenderer to arrange that subsequent requests joing the running job. Also 
timeout the running job, serve a stale version, and inform the client that it's 
better to come back later.

Modified: mmbase/trunk/src/org/mmbase/framework/CachedRenderer.java
===================================================================
--- mmbase/trunk/src/org/mmbase/framework/CachedRenderer.java   2009-05-13 
20:38:28 UTC (rev 35144)
+++ mmbase/trunk/src/org/mmbase/framework/CachedRenderer.java   2009-05-13 
20:40:21 UTC (rev 35145)
@@ -13,9 +13,12 @@
 import java.io.*;
 import java.net.*;
 import java.util.*;
+import java.util.concurrent.*;
 import java.util.regex.*;
 
+import javax.servlet.http.HttpServletRequest;
 
+
 import org.mmbase.util.functions.*;
 import org.mmbase.util.*;
 import org.mmbase.module.core.MMBase;
@@ -61,6 +64,7 @@
     private int expires = -1; // use last modified
 
     private int timeout = 2000; // ms
+    private int wait    = Integer.MAX_VALUE; // ms
 
     private String directory = "CachedRenderer";
 
@@ -74,10 +78,24 @@
         directory = d;
     }
 
+    /**
+     * If using an HttpURLConnection, then use the given timeout. Defaults to 
2 seconds.
+     * @param t Timeout in milliseconds
+     */
     public void setTimeout(int  t) {
         timeout = t;
     }
 
+    /**
+     * If rendering of the cached renderer takes very long, you may choose to 
not wait for the
+     * result. But serve an message or the old version. The job can be joined 
later.
+     *
+     * @param t Wait-time in milliseconds
+     */
+    public void setWait(int  t) {
+        wait = t;
+    }
+
     public void setIncludeRenderTime(String type) {
         includeRenderTime = type;
     }
@@ -161,16 +179,86 @@
         }
     }
 
+    private static final Map<File, Future<Exception>> rendering = new 
ConcurrentHashMap<File, Future<Exception>>();
+
     /**
      * Renders the wrapped renderer, and writes the result to both a file, and 
to the writer.
      */
-    protected void renderWrappedAndFile(File f, Parameters blockParameters, 
Writer w, RenderHints hints) throws FrameworkException, IOException  {
+    protected void renderWrappedAndFile(final File f, final Parameters 
blockParameters, final Writer w, final RenderHints hints, final Runnable ready) 
throws FrameworkException, IOException  {
         writeRenderTime(new Date(), w);
-        Writer fw = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
-        ChainedWriter chain = new ChainedWriter(w, fw);
-        getWraps().render(blockParameters, chain, hints);
-        chain.flush();
-        fw.close();
+
+        Future<Exception> future = rendering.get(f);
+        if (future == null)  {
+            final Parameters myBlockParameters = 
Utils.fixateParameters(blockParameters);
+            future = ThreadPools.jobsExecutor.submit(new Callable<Exception>() 
{
+                    public Exception call() {
+                        try {
+                            File tempFile = new File(f + ".busy");
+                            Writer fw = new OutputStreamWriter(new 
FileOutputStream(tempFile), "UTF-8");
+                            Writer writer;
+                            if (wait == Integer.MAX_VALUE) {
+                                writer = new ChainedWriter(w, fw);
+                            } else {
+                                writer = fw;
+                            }
+                            getWraps().render(myBlockParameters, writer, 
hints);
+                            writer.flush();
+                            fw.close();
+                            tempFile.renameTo(f);
+                            if (ready != null) {
+                                ready.run();
+                            }
+                            log.info("Created " + f);
+                            return null;
+                        } catch (Exception e) {
+                            return e;
+                        }
+                    }
+                });
+            rendering.put(f, future);
+            log.info("Now rendering " + rendering);
+        } else {
+            log.info("Joined " + f + "" + future);
+        }
+        Exception e;
+        try {
+            e = future.get(wait, TimeUnit.MILLISECONDS);
+            if (e == null) {
+                if (wait < Integer.MAX_VALUE) {
+                    renderFile(f, w);
+                }
+                if (rendering.remove(f) != null) {
+                    log.info("Now rendering " + rendering);
+                }
+            }
+        } catch (TimeoutException to) {
+            log.debug(to);
+            w.write("<div class='mm_c stale'>");
+            if (f.exists()) {
+                w.write("<h1>What you see is stale. A new version is being 
rendered. Please try again later.</h1>");
+                renderFile(f, w);
+            } else {
+                w.write("<h1>Rendering took too long. This job is still 
running. Please try again later.</h1>");
+            }
+            w.write("</div>");
+            e = null;
+        } catch (InterruptedException ioe) {
+            throw new FrameworkException(ioe);
+        } catch (ExecutionException ee) {
+            throw new FrameworkException(ee);
+        }
+
+        if (e != null) {
+            if (e instanceof FrameworkException) {
+                throw (FrameworkException) e;
+            }
+            if (e instanceof IOException) {
+                throw (IOException) e;
+            }
+            throw new FrameworkException(e);
+        }
+
+
     }
 
     @Override public void render(Parameters blockParameters, Writer w, 
RenderHints hints) throws FrameworkException {
@@ -180,7 +268,7 @@
             if (expires > 0) {
                 // use expires
                 if (! cacheFile.exists() || ( (cacheFile.lastModified() + 
expires * 1000) < System.currentTimeMillis())) {
-                    renderWrappedAndFile(cacheFile, blockParameters, w, hints);
+                    renderWrappedAndFile(cacheFile, blockParameters, w, hints, 
null);
                 } else {
                     renderFile(cacheFile, w);
                 }
@@ -190,12 +278,20 @@
                 if (uri == null) throw new FrameworkException("" + getWraps() 
+ " did not return an URI, and cannot be cached using getLastModified");
                 URLConnection connection =  uri.toURL().openConnection();
                 connection.setConnectTimeout(timeout);
-                String etag = connection.getHeaderField("ETag");
+                final String etag = connection.getHeaderField("ETag");
                 if (etag != null) {
-                    File etagFile = getETagFile(cacheFile);
+                    final File etagFile = getETagFile(cacheFile);
                     if ( ! cacheFile.exists() || ! etagFile.exists() || ! 
etag.equals(readETag(etagFile))) {
-                        renderWrappedAndFile(cacheFile, blockParameters, w, 
hints);
-                        writeETag(etagFile, etag);
+                        renderWrappedAndFile(cacheFile, blockParameters, w, 
hints, new Runnable() {
+                                public void run()  {
+                                    try {
+                                        writeETag(etagFile, etag);
+                                    } catch (IOException ioe) {
+                                        throw new RuntimeException(ioe);
+                                    }
+                                }
+                            });
+
                     } else {
                         renderFile(cacheFile, w);
                     }
@@ -203,7 +299,7 @@
                     long modified = connection.getLastModified();
                     if (! cacheFile.exists() || (cacheFile.lastModified() < 
modified)) {
                         log.service("Rendering " + uri + " because " + 
cacheFile + " older than " + new Date(modified));
-                        renderWrappedAndFile(cacheFile, blockParameters, w, 
hints);
+                        renderWrappedAndFile(cacheFile, blockParameters, w, 
hints, null);
                     } else {
                         renderFile(cacheFile, w);
                     }

Modified: mmbase/trunk/src/org/mmbase/framework/DocumentationRenderer.java
===================================================================
--- mmbase/trunk/src/org/mmbase/framework/DocumentationRenderer.java    
2009-05-13 20:38:28 UTC (rev 35144)
+++ mmbase/trunk/src/org/mmbase/framework/DocumentationRenderer.java    
2009-05-13 20:40:21 UTC (rev 35145)
@@ -52,6 +52,7 @@
 
     public DocumentationRenderer(String t, Block parent) {
         super(t, parent);
+        setWait(5000);
     }
     @Override public Renderer getWraps() {
         if (wrapped == null) {

Modified: mmbase/trunk/src/org/mmbase/framework/Utils.java
===================================================================
--- mmbase/trunk/src/org/mmbase/framework/Utils.java    2009-05-13 20:38:28 UTC 
(rev 35144)
+++ mmbase/trunk/src/org/mmbase/framework/Utils.java    2009-05-13 20:40:21 UTC 
(rev 35145)
@@ -103,4 +103,35 @@
         XSLTransformer.transform(xml, xsl, res, params);
     }
 
+
+    /**
+     * @since MMBase-1.9.1
+     */
+    public static Parameters fixateParameters(Parameters parameters) {
+        final Parameters myParameters = new Parameters(parameters);
+        // CLone parameters, because after time-out they can otherwise be 
changed by client.
+        // This stuff should problbably be moved to a method of Framework, 
stince Framework specific
+        // stuff is happening.
+        log.debug("" + parameters + " -> " + myParameters);
+        HttpServletRequest req = 
myParameters.get(Parameter.REQUEST);//BasicUrlConverter.getUserRequest(myParameters.get(Parameter.REQUEST));
+        if (req != null) {
+            HttpServletRequest myreq = new 
LocalHttpServletRequest(org.mmbase.module.core.MMBaseContext.getServletContext(),
 "", req.getServletPath());
+            log.debug("" + req.getServletPath());
+            for (Object attrName : Collections.list(req.getAttributeNames())) {
+                String a = (String) attrName;
+                if (a.equals(org.mmbase.framework.basic.State.KEY)) {
+                    new org.mmbase.framework.basic.State(myreq, 
(org.mmbase.framework.basic.State) 
req.getAttribute(org.mmbase.framework.basic.State.KEY));
+                } else {
+                    myreq.setAttribute(a, req.getAttribute(a));
+                }
+            }
+
+            myreq.getParameterMap().putAll(req.getParameterMap());
+            log.debug("atts " + Collections.list(myreq.getAttributeNames()));
+            log.debug("params " + myreq.getParameterMap());
+            myParameters.set(Parameter.REQUEST, myreq);
+        }
+        return myParameters;
+    }
+
 }

Modified: mmbase/trunk/src/org/mmbase/framework/basic/State.java
===================================================================
--- mmbase/trunk/src/org/mmbase/framework/basic/State.java      2009-05-13 
20:38:28 UTC (rev 35144)
+++ mmbase/trunk/src/org/mmbase/framework/basic/State.java      2009-05-13 
20:40:21 UTC (rev 35145)
@@ -79,6 +79,23 @@
         request.setAttribute(KEY, this);
     }
 
+    public State(javax.servlet.http.HttpServletRequest r, State s) {
+        request = r;
+        previousState = s.previousState == null ? null : new State(r, 
s.previousState);
+        depth = s.depth;
+        count = s.count;
+        id = s.id;
+        renderer = s.renderer;
+        type = s.type;
+        processor = s.processor;
+        processed = s.processed;
+        frameworkParameters = new Parameters(s.frameworkParameters);
+        frameworkParameters.setIfDefined(Parameter.REQUEST, r);
+        originalLocalizationContext = s.originalLocalizationContext;
+        action = s.action;
+        request.setAttribute(KEY, this);
+    }
+
     /**
      * The current window state of rendering. As yet unimplemented.
      * @todo

_______________________________________________
Cvs mailing list
[email protected]
http://lists.mmbase.org/mailman/listinfo/cvs

Reply via email to