portals-jetspeed-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tay...@apache.org
Subject svn commit: r496514 - in /portals/jetspeed-2/trunk: components/portal/src/java/org/apache/jetspeed/aggregator/impl/ jetspeed-api/src/java/org/apache/jetspeed/aggregator/ src/webapp/WEB-INF/assembly/
Date Mon, 15 Jan 2007 22:01:14 GMT
Author: taylor
Date: Mon Jan 15 14:01:14 2007
New Revision: 496514

URL: http://svn.apache.org/viewvc?view=rev&rev=496514
Log:
contribution from Woonsan Ko:

Added 'deployment hints' and 'timeout option' features as attached.
In my modification, I expected the deployment hint put like the following:

    <portlet>
        <portlet-name>PickANumberPortlet</portlet-name>

        <js:metadata name="timeout">4000</js:metadata> <!-- ms -->

    </portlet>

I used the metadata tag for simplicity. 
Currently, I think that a positive number for timeout means requiring a thread automatically.

Because our unit test is incomplete, I tested this on a production server.
My tests were as follows:

 - First, in the /WEB-INF/assembly/pipelines.xml, I replaced
'org.apache.jetspeed.aggregator.PageAggregator' with
'org.apache.jetspeed.aggregator.AsyncPageAggregator' for the 'aggregatorValve' bean constructor
argument.
 - Second, to test an asynchronous portlet rendering, I added the timeout hints for the
PickANumber portlet in the /demo/WEB-INF/jetspeed-portlet.xml, as mentioned above.
   In this case, only the PickANumber portlet is asynchronously rendered, and the others are
synchronously rendered.
   Also, to test the PickANumber portlet to be interrupted, I added a line like
'Thread.sleep(5000);' in the /demo/WEB-INF/demo/simple/PickANumber.jsp'.
 - Third, to test all portlets asynchronous-rendering, I modified the default portlet timeout
constructor argument to 4000 for the 'org.apache.jetspeed.aggregator.PortletRenderer' bean
in the
/WEB-INF/assembly/aggregation.xml.
   In this case, all portlets are asynchronously rendered.

On my implementations:
 - I added some methods and properties to the RenderingJob for timeout.
 - The WorkerMonitorImpl has a RenderingJobTimeoutMonitor thread, which will check timeouts
periodically.
   If an asynchronous portlet exceeds the time limit, the RenderingJobTimeoutMonitor just
interrupt the worker thread for the job. So, a worker thread is killed, and a new fresh worker
may
be created by getWorker() method of WorkerMonitorImpl if necessary.


Modified:
    portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/AsyncPageAggregatorImpl.java
    portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/PortletRendererImpl.java
    portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/RenderingJobImpl.java
    portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/WorkerMonitorImpl.java
    portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/aggregator/RenderingJob.java
    portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/aggregation.xml

Modified: portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/AsyncPageAggregatorImpl.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/AsyncPageAggregatorImpl.java?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/AsyncPageAggregatorImpl.java
(original)
+++ portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/AsyncPageAggregatorImpl.java
Mon Jan 15 14:01:14 2007
@@ -188,8 +188,12 @@
                     {
                         // kick off render thread
                         // and store the portlet rendering job into the portlet jobs list.
-                        RenderingJob job = renderer.render(child, context); 
-                        portletJobs.add(job);
+                        RenderingJob job = renderer.render(child, context);
+
+                        if (job.getTimeout() > 0) 
+                        {
+                            portletJobs.add(job);
+                        }
                     }
                     else
                     {

Modified: portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/PortletRendererImpl.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/PortletRendererImpl.java?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/PortletRendererImpl.java
(original)
+++ portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/PortletRendererImpl.java
Mon Jan 15 14:01:14 2007
@@ -17,6 +17,8 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Collection;
+import java.util.Iterator;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -37,6 +39,9 @@
 import org.apache.jetspeed.container.window.FailedToRetrievePortletWindow;
 import org.apache.jetspeed.container.window.PortletWindowAccessor;
 import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
+import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
+import org.apache.jetspeed.om.common.GenericMetadata;
+import org.apache.jetspeed.om.common.LocalizedField;
 import org.apache.jetspeed.om.page.ContentFragment;
 import org.apache.jetspeed.request.RequestContext;
 import org.apache.jetspeed.services.title.DynamicTitleService;
@@ -65,18 +70,30 @@
     protected PortletWindowAccessor windowAccessor;
     protected PortalStatistics statistics;
     protected DynamicTitleService addTitleService;
+    protected long defaultPortletTimeout;
 
     public PortletRendererImpl(PortletContainer container, 
                                PortletWindowAccessor windowAccessor,
                                WorkerMonitor workMonitor,
                                PortalStatistics statistics,
-                               DynamicTitleService addTitleService)
+                               DynamicTitleService addTitleService,
+                               long defaultPortletTimeout)
     {
         this.container = container;
         this.windowAccessor = windowAccessor;
         this.workMonitor = workMonitor;
         this.statistics = statistics;
         this.addTitleService = addTitleService;
+        this.defaultPortletTimeout = defaultPortletTimeout;
+    }
+
+    public PortletRendererImpl(PortletContainer container, 
+                               PortletWindowAccessor windowAccessor,
+                               WorkerMonitor workMonitor,
+                               PortalStatistics statistics,
+                               DynamicTitleService addTitleService)
+    {
+        this( container, windowAccessor, workMonitor, statistics, null, 0 );
     }
 
     public PortletRendererImpl(PortletContainer container, 
@@ -197,8 +214,17 @@
             portletWindow = getPortletWindow(fragment);
             servletRequest = requestContext.getRequestForWindow(portletWindow);
             servletResponse = dispatcherCtrl.getResponseForWindow(portletWindow, requestContext);
-            rJob = buildRenderingJob(fragment, servletRequest, servletResponse, requestContext,
true);                
-            workMonitor.process(rJob);
+            rJob = buildRenderingJob(fragment, servletRequest, servletResponse, requestContext,
true);
+
+            if (rJob.getTimeout() > 0) 
+            {
+                workMonitor.process(rJob);
+            } 
+            else 
+            {
+                rJob.execute();
+            }
+
             addTitleToHeader( portletWindow, fragment, servletRequest, servletResponse );
         }
         catch (Exception e1)
@@ -250,13 +276,15 @@
     }
 
     protected RenderingJob buildRenderingJob( ContentFragment fragment, HttpServletRequest
request,
-        HttpServletResponse response, RequestContext requestContext, boolean isParallel )

-    throws FailedToRetrievePortletWindow, FailedToRenderFragmentException, PortletEntityNotStoredException
+                                              HttpServletResponse response, RequestContext
requestContext, boolean isParallel ) 
+        throws FailedToRetrievePortletWindow, FailedToRenderFragmentException, PortletEntityNotStoredException
     {
         RenderingJob rJob = null;
         ContentDispatcher dispatcher = null;
         
         PortletWindow portletWindow = getPortletWindow(fragment);
+        PortletDefinitionComposite portletDefinition = 
+            (PortletDefinitionComposite) portletWindow.getPortletEntity().getPortletDefinition();
         ContentDispatcherCtrl dispatcherCtrl = getDispatcherCtrl(requestContext, true);
         dispatcher = getDispatcher(requestContext, true);        
         request = requestContext.getRequestForWindow(portletWindow);
@@ -271,7 +299,7 @@
         request.setAttribute(PortalReservedParameters.PORTLET_WINDOW_ATTRIBUTE, portletWindow);
         PortletContent portletContent = dispatcher.getPortletContent(fragment);
         fragment.setPortletContent(portletContent);
-        
+
         // In case of parallel mode, store attributes in a map to be refered by worker.
         if (isParallel)
         {
@@ -285,8 +313,7 @@
 
             // the portlet invoker is not thread safe; it stores current portlet definition
as a member variable.
             // so, store portlet definition as an attribute of worker
-            workerAttrs.put(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE, 
-                            portletWindow.getPortletEntity().getPortletDefinition());
+            workerAttrs.put(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE, portletDefinition);
 
             rJob = new RenderingJobImpl(container, portletContent, fragment, request, response,
requestContext, portletWindow, statistics, workerAttrs);
         }
@@ -295,6 +322,37 @@
             rJob = new RenderingJobImpl(container, portletContent, fragment, request, response,
requestContext, portletWindow, statistics);
         }
 
+        long timeoutMetadata = 0;
+
+        Collection timeoutFields = portletDefinition.getMetadata().getFields("timeout");
+
+        if (timeoutFields != null) 
+        {
+            Iterator it = timeoutFields.iterator();
+
+            if (it.hasNext()) 
+            {
+                LocalizedField timeoutField = (LocalizedField) timeoutFields.iterator().next();
+
+                try 
+                {
+                    timeoutMetadata = Long.parseLong(timeoutField.getValue());
+                }
+                catch (NumberFormatException nfe) 
+                {
+                    log.warn("Invalid timeout metadata: " + nfe.getMessage());
+                }
+            }
+        }
+
+        if (timeoutMetadata > 0) 
+        {
+            rJob.setTimeout(timeoutMetadata);
+        } 
+        else if (this.defaultPortletTimeout > 0) 
+        {
+            rJob.setTimeout(this.defaultPortletTimeout);
+        }
 
         return rJob;
         

Modified: portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/RenderingJobImpl.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/RenderingJobImpl.java?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/RenderingJobImpl.java
(original)
+++ portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/RenderingJobImpl.java
Mon Jan 15 14:01:14 2007
@@ -65,6 +65,9 @@
     protected PortalStatistics statistics;
 
     protected Map workerAttributes;
+
+    protected long startTimeMillis = 0;
+    protected long timeout;
     
     public RenderingJobImpl(PortletContainer container, 
                             PortletContent portletContent, 
@@ -102,6 +105,31 @@
     }
 
     /**
+     * Sets portlet timout in milliseconds.
+     */
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+    /**
+     * Gets portlet timout in milliseconds.
+     */
+    public long getTimeout() {
+        return this.timeout;
+    }
+
+    /**
+     * Checks if the portlet rendering is timeout
+     */
+    public boolean isTimeout() {
+        if ((this.timeout > 0) && (this.startTimeMillis > 0)) {
+            return (System.currentTimeMillis() - this.startTimeMillis > this.timeout);
+        }
+
+        return false;
+    }
+
+    /**
      * Checks if queue is empty, if not try to empty it by calling
      * the WorkerMonitor. When done, pause until next scheduled scan.
      */
@@ -109,6 +137,10 @@
     {       
         try
         {
+            if (this.timeout > 0) {
+                this.startTimeMillis = System.currentTimeMillis();
+            }
+
             // A little baby hack to make sure the worker thread has PortletContent to write
too.
             fragment.setPortletContent(portletContent);
             execute();                     
@@ -183,11 +215,10 @@
         }
         catch (Throwable t)
         {
-            // this will happen is request is prematurely aborted
-            if ( t instanceof UnavailableException)
+            if (t instanceof UnavailableException)
             {
                 // no need to dump a full stack trace to the log
-                log.error("Error rendering portlet OID "+this.window.getId()+": "+t.toString());
+                log.error("Error rendering portlet OID " + this.window.getId() + ": " + t.toString());
             }
             else
             {

Modified: portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/WorkerMonitorImpl.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/WorkerMonitorImpl.java?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/WorkerMonitorImpl.java
(original)
+++ portals/jetspeed-2/trunk/components/portal/src/java/org/apache/jetspeed/aggregator/impl/WorkerMonitorImpl.java
Mon Jan 15 14:01:14 2007
@@ -21,15 +21,21 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Stack;
+import java.util.LinkedList;
+import java.util.Collections;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.jetspeed.aggregator.RenderingJob;
 import org.apache.jetspeed.aggregator.Worker;
 import org.apache.jetspeed.aggregator.WorkerMonitor;
+import org.apache.jetspeed.aggregator.PortletContent;
 import org.apache.jetspeed.util.Queue;
 import org.apache.jetspeed.util.FIFOQueue;
 
+import org.apache.pluto.om.window.PortletWindow;
+import org.apache.pluto.om.common.ObjectID;
+
 /**
  * The WorkerMonitor is responsible for dispatching jobs to workers
  * It uses an Apache HTTPd configuration style of min/max/spare workers
@@ -81,10 +87,19 @@
     /** Job queue */
     protected Queue queue;
 
+    /** Workers to be monitored for timeout checking */
+    protected List workersMonitored = Collections.synchronizedList(new LinkedList());
+
+    /** Renering Job Timeout monitor */
+    protected RenderingJobTimeoutMonitor jobMonitor;
+
     public void start()
     {
         addWorkers(this.minWorkers);
         setQueue(new FIFOQueue());
+
+        jobMonitor = new RenderingJobTimeoutMonitor(1000);
+        jobMonitor.start();
     }
 
     public void stop()
@@ -170,6 +185,11 @@
                 synchronized (worker)
                 {
                     worker.setJob(job, context);
+
+                    if (job.getTimeout() > 0) {
+                        workersMonitored.add(worker);
+                    }
+
                     worker.notify();
                     runningJobs++;
                 }
@@ -191,6 +211,8 @@
         // backlog job to this worker, else reset job count and put
         // it on the idle queue.
 
+        long jobTimeout = ((RenderingJob) worker.getJob()).getTimeout();
+
         synchronized (worker)
         {
             if ((worker.getJobCount()<this.maxJobsPerWorker)&&(queue.size()>0))
@@ -209,6 +231,10 @@
             }
         }
 
+        if (jobTimeout > 0) {
+            workersMonitored.remove(worker);
+        }
+
         synchronized (this.workers)
         {
             this.workers.push(worker);
@@ -234,4 +260,78 @@
         return this.tg.activeCount();
     }
     
+    class RenderingJobTimeoutMonitor extends Thread {
+
+        long interval = 1000;
+
+        RenderingJobTimeoutMonitor(long interval) {
+            super("RenderingJobTimeoutMonitor");
+
+            if (interval > 0) {
+                this.interval = interval;
+            }
+        }
+
+        public void run() {
+            while (true) {
+                try {
+                    int size = workersMonitored.size();
+
+                    for (int i = 0; i < size; i++) {
+                        WorkerImpl worker = (WorkerImpl) workersMonitored.get(i);
+
+                        if (null == worker) {
+                            break;
+                        }
+
+                        RenderingJob job = (RenderingJob) worker.getJob();
+
+                        if (null != job) {
+                            if (job.isTimeout()) {
+                                killJob(worker, job);
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("Exception during job monitoring.", e);
+                }
+               
+                try {
+                    synchronized (this) {
+                        wait(this.interval);
+                    }
+                } catch (InterruptedException e) {
+                    ;
+                }
+            }
+        }
+
+        public void killJob(WorkerImpl worker, RenderingJob job) {
+            try {
+                if (log.isWarnEnabled()) {
+                    PortletWindow window = job.getWindow();
+                    ObjectID windowId = (null != window ? window.getId() : null);
+                    log.warn("Portlet Rendering job to be interrupted by timeout (" + job.getTimeout()
+ "ms): " + windowId);
+                }
+
+                int waitCount = 0;
+                PortletContent content = job.getPortletContent();
+
+                while (!content.isComplete()) {
+                    if (++waitCount > 10) {
+                        break;
+                    }
+
+                    worker.interrupt();
+
+                    synchronized (content) {
+                        content.wait();
+                    }
+                }
+            } catch (Exception e) {
+                log.error("Exceptiong during job killing.", e);
+            }
+        }
+
+    }
 }

Modified: portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/aggregator/RenderingJob.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/aggregator/RenderingJob.java?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/aggregator/RenderingJob.java
(original)
+++ portals/jetspeed-2/trunk/jetspeed-api/src/java/org/apache/jetspeed/aggregator/RenderingJob.java
Mon Jan 15 14:01:14 2007
@@ -33,6 +33,11 @@
     PortletWindow getWindow(); 
 
     PortletContent getPortletContent();
-        
+
+    void setTimeout(long portletTimeout);
+
+    long getTimeout();
+
+    boolean isTimeout();
 }
 

Modified: portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/aggregation.xml
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/aggregation.xml?view=diff&rev=496514&r1=496513&r2=496514
==============================================================================
--- portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/aggregation.xml (original)
+++ portals/jetspeed-2/trunk/src/webapp/WEB-INF/assembly/aggregation.xml Mon Jan 15 14:01:14
2007
@@ -45,6 +45,15 @@
         <constructor-arg>
             <ref bean="PortalStatistics" />
         </constructor-arg>                        
+        <constructor-arg>
+            <null/>
+        </constructor-arg>
+        <!-- Default portlet timeout in milliseconds:
+        Zero means no portlet timeout option by default.
+        -->
+        <constructor-arg>
+            <value>0</value>
+        </constructor-arg>        
     </bean>
 
     <!-- Portlet Renderer w/title in http response header -->



---------------------------------------------------------------------
To unsubscribe, e-mail: jetspeed-dev-unsubscribe@portals.apache.org
For additional commands, e-mail: jetspeed-dev-help@portals.apache.org


Mime
View raw message