Return-Path: Mailing-List: contact cocoon-dev-help@xml.apache.org; run by ezmlm Delivered-To: mailing list cocoon-dev@xml.apache.org Received: (qmail 4420 invoked from network); 16 Sep 2000 18:00:09 -0000 Received: from rdu25-20-164.nc.rr.com (HELO ma7.webslingerZ.com) (@24.25.20.164) by locus.apache.org with SMTP; 16 Sep 2000 18:00:09 -0000 Received: by ma7.webslingerZ.com (Postfix, from userid 501) id 068D1480F; Sat, 16 Sep 2000 14:02:29 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by ma7.webslingerZ.com (Postfix) with ESMTP id DF20C6087 for ; Sat, 16 Sep 2000 14:02:29 -0400 (EDT) Date: Sat, 16 Sep 2000 14:02:29 -0400 (EDT) From: Donald Ball X-Sender: balld@localhost.localdomain To: cocoon-dev@xml.apache.org Subject: woah there - "vetoed" changes committed to Engine Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII X-Spam-Rating: locus.apache.org 1.6.2 0/1000/N robin, i'm psyched to see another committer dedicated to keeping the cocoon1 branch up and running until cocoon2 comes around, but i got a little issue here. i voted -1 on the engine synch. patch since i thought that we shouldn't put in a patch that messes around with cocoon that deeply shortly before a new major release. according to the terms of the asf project constitution: http://java.apache.org/main/constitution.html my veto is binding unless you convince me otherwise. i'm really not trying to be a tight-ass, i'm perfectly happy to switch my vote, but we oughta follow the rules - especially now that there are now, what, six, seven whole committers on the cocoon project? :) (note i'm speaking from experience here, having introduced a fairly serious synchronization bug shortly before a major release of cocoon. was it 1.3?) - donald ---------- Forwarded message ---------- Date: 16 Sep 2000 16:04:31 -0000 From: greenrd@locus.apache.org Reply-To: cocoon-dev@xml.apache.org To: xml-cocoon-cvs@apache.org Subject: cvs commit: xml-cocoon/src/org/apache/tools DOMWriter.java greenrd 00/09/16 09:04:31 Modified: . changes.xml src/org/apache/cocoon cocoon.properties Defaults.java Engine.java Added: samples/profiler profiler.xml profiler.xsl src/org/apache/cocoon Profiler.java src/org/apache/tools DOMWriter.java Log: Fixed synchronization bugs in Engine and added profiler to Engine - totally unrelated\! Revision Changes Path 1.109 +19 -6 xml-cocoon/changes.xml Index: changes.xml =================================================================== RCS file: /home/cvs/xml-cocoon/changes.xml,v retrieving revision 1.108 retrieving revision 1.109 diff -u -r1.108 -r1.109 --- changes.xml 2000/09/15 04:38:13 1.108 +++ changes.xml 2000/09/16 16:04:28 1.109 @@ -4,7 +4,7 @@ @@ -13,14 +13,23 @@ + + + Added very primitive profiler (see cocoon.properties) + + + Fixed some synchronization errors in Engine. You can now call a Cocoon + page from a Cocoon page, if you really want (this is inefficient and a + bad architecture, but it's possible.) + - added xspdoc comments to esql logicsheet and added xspdoc to document convertor in the xml.apache.org site skin directory. god only knows how i'm supposed to add it to the build procedure... help? + Added xspdoc comments to esql logicsheet and added xspdoc to document convertor in the xml.apache.org site skin directory. god only knows how i'm supposed to add it to the build procedure... help? - added error handling to esql logicsheet and documented its use in esql sample + Added error handling to esql logicsheet and documented its use in esql sample. Fixed encoding problem with xinclude processor @@ -28,11 +37,15 @@ Fixed problem with XSP and namespaces (now follows the correct name="xml-stylesheet" syntax) + + Upgraded Xerces to 1.2 because previous version had a bug which meant it couldn't build + the Cocoon documentation. + Added esql logicsheet - - Upgraded xalan to 1_2_D01 + + Upgraded xalan to 1_2_D02 Added installation instructions for iPlanet. @@ -53,7 +66,7 @@ Patched the cookie XSP taglib and the LDAP processor. - Make Cocoon running better under heavy load. + Make Cocoon run better under heavy load. Added Solaris8 and improved Win2k installation case documentation. 1.1 xml-cocoon/samples/profiler/profiler.xml Index: profiler.xml =================================================================== org.apache.cocoon.Profiler private Profiler profiler; public void init (Director director) { super.init (director); profiler = (Profiler) director.getActor ("profiler"); } if (profiler == null) { throw new RuntimeException ("Profiling has not been enabled in this instance of Apache Cocoon. \n" + "It can be enabled in the cocoon.properties file."); } profiler.getProfileTable (document) 1.1 xml-cocoon/samples/profiler/profiler.xsl Index: profiler.xsl =================================================================== Profiler Table
1.38 +15 -4 xml-cocoon/src/org/apache/cocoon/cocoon.properties Index: cocoon.properties =================================================================== RCS file: /home/cvs/xml-cocoon/src/org/apache/cocoon/cocoon.properties,v retrieving revision 1.37 retrieving revision 1.38 diff -u -r1.37 -r1.38 --- cocoon.properties 2000/08/18 22:41:34 1.37 +++ cocoon.properties 2000/09/16 16:04:29 1.38 @@ -120,10 +120,9 @@ # Default encoding to be used for code generation and compilation # If omitted, the platform's default encoding will be used -# This encoding should be used in: +# This encoding should also be used in: # - The XSP document declaration -# - The XSLT stylesheet "encoding" attribute -# - Cocoon's default formatter "encoding" configuration property +# - The "encoding" configuration property of the formatter to be used # Example: Russian uses "Cp1251" # processor.xsp.encoding = Cp1251 @@ -319,7 +318,7 @@ # Indicates the thread priority (1-10: 10 is maximum, 1 is minimum) #store.threadpriority = 10 -# Uncomment this to disable the background thread that manages the cache +# Uncomment this to disable the background thread that manages the cache # overflow and leave this at request time. #store.usethread = false @@ -368,3 +367,15 @@ browser.12 = mozilla5=Netscape6/ browser.13 = netscape=Mozilla +########################################## +# Profiling # +########################################## + +#Uncomment this to enable coarse performance profiling. +#Look at samples/profiler/profiler.xml to see the results of the profiler + +#profiler.enabled=true + +#Comment this out to remove the <--this page was served ... comment from the +#end of text pages served by Cocoon +verbosity=true 1.13 +7 -4 xml-cocoon/src/org/apache/cocoon/Defaults.java Index: Defaults.java =================================================================== RCS file: /home/cvs/xml-cocoon/src/org/apache/cocoon/Defaults.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- Defaults.java 2000/05/01 23:53:16 1.12 +++ Defaults.java 2000/09/16 16:04:29 1.13 @@ -1,4 +1,4 @@ -/*-- $Id: Defaults.java,v 1.12 2000/05/01 23:53:16 stefano Exp $ -- +/*-- $Id: Defaults.java,v 1.13 2000/09/16 16:04:29 greenrd Exp $ -- ============================================================================ The Apache Software License, Version 1.1 @@ -54,7 +54,7 @@ * The Cocoon strings. * * @author Stefano Mazzocchi - * @version $Revision: 1.12 $ $Date: 2000/05/01 23:53:16 $ + * @version $Revision: 1.13 $ $Date: 2000/09/16 16:04:29 $ */ public interface Defaults { @@ -66,7 +66,6 @@ public static final String INIT_ARG = "properties"; public static final String PROPERTIES = "cocoon.properties"; - public static final String INTERNAL_PROPERTIES = "org/apache/cocoon/" + PROPERTIES; public static final String HOME = "document.root"; public static final String SHOW_STATUS = "selfservlet.enabled"; @@ -83,6 +82,8 @@ public static final String CACHE_DEFAULT = "org.apache.cocoon.cache.CocoonCache"; public static final String STORE_PROP = "store"; public static final String STORE_DEFAULT = "org.apache.cocoon.store.CocoonStore"; + public static final String PROFILER_PROP = "profiler"; + public static final String PROFILER_DEFAULT = "org.apache.cocoon.Profiler"; public static final String PRODUCER_PROP = "producer"; public static final String PROCESSOR_PROP = "processor"; @@ -98,7 +99,9 @@ public static final int LOOPS = 10; - public static final boolean VERBOSE = @verbose@; public static final boolean LOG = @log@; + // Profiler event types. These need to be valid XML names - no spaces. + public static final String WHOLE_REQUEST = "Whole-request", + OUTPUTTING = "Outputting"; } 1.34 +162 -135 xml-cocoon/src/org/apache/cocoon/Engine.java Index: Engine.java =================================================================== RCS file: /home/cvs/xml-cocoon/src/org/apache/cocoon/Engine.java,v retrieving revision 1.33 retrieving revision 1.34 diff -u -r1.33 -r1.34 --- Engine.java 2000/08/17 22:34:13 1.33 +++ Engine.java 2000/09/16 16:04:30 1.34 @@ -1,4 +1,4 @@ -/*-- $Id: Engine.java,v 1.33 2000/08/17 22:34:13 stefano Exp $ -- +/*-- $Id: Engine.java,v 1.34 2000/09/16 16:04:30 greenrd Exp $ -- ============================================================================ The Apache Software License, Version 1.1 @@ -48,7 +48,7 @@ Software Foundation, please see . */ - + package org.apache.cocoon; import java.io.*; @@ -74,14 +74,16 @@ * This class implements the engine that does all the document processing. * * @author Stefano Mazzocchi - * @version $Revision: 1.33 $ $Date: 2000/08/17 22:34:13 $ + * @author Robin Green + * @version $Revision: 1.34 $ $Date: 2000/09/16 16:04:30 $ */ public class Engine implements Defaults { private Block blocker = new Block(); - - private static Hashtable engineInstances = new Hashtable(1, 0.90f); + private boolean VERBOSE, PROFILE; + + private static Hashtable engineInstances = new Hashtable(2, 0.90f); Configurations configurations; @@ -99,6 +101,7 @@ Cache cache; Store store; Logger logger; + Profiler profiler; ServletContext servletContext; @@ -181,6 +184,16 @@ "org.apache.cocoon.Browsers", configurations.getConfigurations(BROWSERS_PROP)); manager.setRole("browsers", browsers); + + VERBOSE = configurations.get ("verbosity", "false").equals ("true"); + + // If enabled, create the profiler and register it + PROFILE = configurations.get ("profiler.enabled", "false").equals ("true"); + if (PROFILE) { + profiler = (Profiler) manager.create((String) configurations.get(PROFILER_PROP, + PROFILER_DEFAULT), configurations.getConfigurations(PROFILER_PROP)); + manager.setRole("profiler", profiler); + } } /** @@ -199,7 +212,7 @@ public static Engine getInstance(Configurations confs, Object context) throws Exception { Engine engine = (Engine) engineInstances.get(context); - + if (engine == null) { synchronized (Engine.class) { engine = new Engine(confs, context); @@ -224,7 +237,7 @@ // return the first engine instance found return (Engine) engineInstances.elements().nextElement(); } - + throw new Exception("The Cocoon engine has not been initialized!"); } @@ -234,6 +247,20 @@ */ public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception { + // get the request flags + boolean CACHE = getFlag(request, "cache", true); + boolean DEBUG = getFlag(request, "debug", false); + boolean VERBOSE = getFlag(request, "verbose", this.VERBOSE); + boolean PROFILE = getFlag(request, "profile", this.PROFILE); + + Profiler.RequestMarker requestMarker = null; + if (PROFILE) { + // We cannot guarantee that the request object will not be recycled by the + // servlet runner, so use requestMarker instead. + requestMarker = new Profiler.RequestMarker (request); + profiler.startEvent (requestMarker, WHOLE_REQUEST); + } + if (LOG) logger.log(this, "Starting request", Logger.INFO); // if verbose mode is on, take a time snapshot for later evaluation @@ -243,10 +270,6 @@ // this may be needed if debug is turned on ByteArrayOutputStream debugStream = null; - // get the request flags - boolean CACHE = getFlag(request, "cache", true); - boolean DEBUG = getFlag(request, "debug", false); - // get the request user agent String agent = request.getParameter("user-Agent"); if (agent == null) agent = request.getHeader("user-Agent"); @@ -264,11 +287,11 @@ } Page page = null; - + boolean lock = false; - + String encodedRequest = Utils.encode( request ); - + try { if ( CACHE ) { // ask if the cache contains the page requested and if it's @@ -278,10 +301,10 @@ // if the page isn't in cache block any furhter access to this page // until it get's put into cache if ( page == null ) { - + // lock this page while we build it and put it in cache lock = this.blocker.lock( encodedRequest ); - + if ( ! lock ) { // we were blocked so by another thread processing this page // so maybe it's in cache now @@ -289,95 +312,101 @@ } } } - + // the page was not found in the cache or the cache was // disabled, we need to process it - - + + if (page == null) { - - synchronized( this ) { - - if (LOG) logger.log(this, "Creating page", Logger.DEBUG); - - // continue until the page is done. - for (int i = 0; i < LOOPS; i++) { - // catch if any OutOfMemoryError is thrown - try { - // create the Page wrapper - page = new Page(); - - // get the document producer - Producer producer = producers.getProducer(request); - - // set the producer as a page changeable point - page.setChangeable(producer); - - // pass the produced stream to the parser - Document document = producer.getDocument(request); - - if (LOG) logger.log(this, "Document produced", Logger.DEBUG); - - // pass needed parameters to the processor pipeline - Hashtable environment = new Hashtable(); - environment.put("path", producer.getPath(request)); - environment.put("browser", browsers.map(agent)); - environment.put("request", request); - environment.put("response", response); - if (servletContext != null) environment.put("context", servletContext); - - // process the document through the document processors - while (true) { - Processor processor = processors.getProcessor(document); - if (processor == null) break; - document = processor.process(document, environment); - page.setChangeable(processor); - if (LOG) logger.log(this, "Document processed", Logger.DEBUG); - } - - // get the right formatter for the page - Formatter formatter = formatters.getFormatter(document); - - // FIXME: I know it sucks to encapsulate a nice stream into - // a long String to push it into the cache. In the future, - // we'll find a smarter way to do it. - - // format the page - StringWriter writer = new StringWriter(); - formatter.format(document, writer, environment); - - if (LOG) logger.log(this, "Document formatted", Logger.DEBUG); - - // fill the page bean with content - page.setContent(writer.toString()); - - // set content type together with encoding if appropriate - String encoding = formatter.getEncoding(); - if (encoding != null) { - page.setContentType(formatter.getMIMEType() + "; charset=" + encoding); - } else { - page.setContentType(formatter.getMIMEType()); - } - - // page is done without memory errors so exit the loop - break; - } catch (OutOfMemoryError e) { - if (LOG) logger.log(this, "Triggered OutOfMemory", Logger.WARNING); - // force the cache to free some of its content. - cache.flush(); - // reset the page to signal the error - page = null; + + if (LOG) logger.log(this, "Creating page", Logger.DEBUG); + + // continue until the page is done. + for (int i = 0; i < LOOPS; i++) { + // catch if any OutOfMemoryError is thrown + try { + // create the Page wrapper + page = new Page(); + + // get the document producer + Producer producer = producers.getProducer(request); + + // set the producer as a page changeable point + page.setChangeable(producer); + + // pass the request object to the producer and produce the initial + // Document + if (PROFILE) profiler.startEvent (requestMarker, producer.getClass ()); + Document document = producer.getDocument(request); + if (PROFILE) profiler.finishEvent (requestMarker, producer.getClass ()); + + if (LOG) logger.log(this, "Document produced", Logger.DEBUG); + + // pass needed parameters to the processor pipeline + Hashtable environment = new Hashtable(); + environment.put("path", producer.getPath(request)); + environment.put("browser", browsers.map(agent)); + environment.put("request", request); + environment.put("response", response); + if (servletContext != null) environment.put("context", servletContext); + + // process the document through the document processors + for (int processNum = 0; true; processNum++) { + Processor processor = processors.getProcessor(document); + if (processor == null) break; + String processDesc = processor.getClass ().getName () + "-" + processNum; + if (PROFILE) profiler.startEvent (requestMarker, processDesc); + document = processor.process(document, environment); + page.setChangeable(processor); + if (PROFILE) profiler.finishEvent (requestMarker, processDesc); + if (LOG) logger.log(this, "Document processed", Logger.DEBUG); } + + // get the right formatter for the page + Formatter formatter = formatters.getFormatter(document); + + // FIXME: I know it sucks to encapsulate a nice stream into + // a long String to push it into the cache. In the future, + // we'll find a smarter way to do it. + + // format the page + if (PROFILE) profiler.startEvent (requestMarker, formatter.getClass ()); + StringWriter writer = new StringWriter(); + formatter.format(document, writer, environment); + if (PROFILE) profiler.finishEvent (requestMarker, formatter.getClass ()); + + if (LOG) logger.log(this, "Document formatted", Logger.DEBUG); + + // fill the page bean with content + if (PROFILE) profiler.startEvent (requestMarker, OUTPUTTING); + page.setContent(writer.toString()); + + // set content type together with encoding if appropriate + String encoding = formatter.getEncoding(); + if (encoding != null) { + page.setContentType(formatter.getMIMEType() + "; charset=" + encoding); + } else { + page.setContentType(formatter.getMIMEType()); + } + + // page is done without memory errors so exit the loop + break; + } catch (OutOfMemoryError e) { + if (LOG) logger.log(this, "Triggered OutOfMemory", Logger.WARNING); + // force the cache to free some of its content. + cache.flush(); + // reset the page to signal the error + page = null; } } } - + if (page == null) { if (LOG) logger.log(this, "System is out of memory", Logger.EMERGENCY); throw new Exception("FATAL ERROR: the system ran out of memory when" + " processing the request. Increase your JVM memory."); } - + if (DEBUG) { // send the debug message and restore the streams Frontend.print(response, "Debugging " + request.getRequestURI(), debugStream.toString()); @@ -386,15 +415,15 @@ } else { // set the response content type response.setContentType(page.getContentType()); - + // get the output writer PrintWriter out = response.getWriter(); - + // send the page out.println(page.getContent()); - + // if verbose mode is on the the output type allows it - // and the HTTP request isn't a HEAD + // and the HTTP request isn't a HEAD // print some processing info as a comment if (VERBOSE && (page.isText()) && !"HEAD".equals(request.getMethod())) { time = System.currentTimeMillis() - time; @@ -404,16 +433,18 @@ + Cocoon.version() + " -->"); //out.println(""); } - + // send all content so that client doesn't wait while caching. out.flush(); + if (PROFILE) profiler.finishEvent (requestMarker, OUTPUTTING); } - + if (LOG) logger.log(this, "response sent to client", Logger.WARNING); - + if (PROFILE) profiler.finishEvent (requestMarker, WHOLE_REQUEST); + // cache the created page. cache.setPage(page, request); - + } finally { // if there is a lock make sure it is released, // otherwise this page could never be served @@ -421,7 +452,7 @@ this.blocker.unlock( encodedRequest ); } } - + } /** @@ -456,7 +487,7 @@ } return table; } - + /** * LAF: August 2, 2000 * @@ -468,72 +499,68 @@ * Solution: If caching is on block requests to build a page if another request * is currently building that same page, i.e. subsequent requests wait until the * page gets put into cache. Also, if the first attempt to build the page fails - * to put the page in cache allow the next queued request to build the page. - * See PersistantCache and PersistantStore for further enhancments to large pages. + * to put the page in cache, allow the next queued request to build the page. + * See PersistantCache and PersistantStore (not currently included in Cocoon) + * for further enhancments to large pages. */ private class Block { // holds the locked objects private Hashtable blocks = new Hashtable(); - - - + + + /** * Checks if a key is being blocked, and if so blocks this request until notified. * If the key is not locked then it becomes locked, so any subsequent calls will block. * * @param key - The object to be locked. - * @return boolean - ture = a lock was obtained, false = the caller was blocked. + * @return boolean - true = a lock was obtained, false = the caller was blocked. */ - public boolean lock( Object key ) + public synchronized boolean lock( Object key ) { boolean locked = false; - + if ( ! this.blocks.containsKey(key) ) { // flag the key as locked - System.err.println( "Locking: " + key ); + if (LOG) logger.log(this, "Locking: " + key, Logger.INFO); this.blocks.put( key, key ); locked = true; - + } else { // block the call - System.err.println( "Blocking: " + key ); + if (LOG) logger.log(this, "Blocking: " + key, Logger.INFO); try { - // synchronization is required to be an owner of this objects monitor - synchronized( this ) { - // wait until the block is released by the blocking thread - while( this.blocks.containsKey(key) ) { - wait(); - } + // wait until the block is released by the blocking thread + while( this.blocks.containsKey(key) ) { + wait(); } } catch( InterruptedException ie ) { - System.err.println( "Wait for '" + key + "' was interrupted" ); + if (LOG) logger.log(this, + "Wait for '" + key + "' was interrupted", Logger.WARNING); } } - - System.err.println( "Total locks for all pages: " + this.blocks.size() ); + + if (LOG) logger.log(this, + "Total locks for all pages: " + this.blocks.size(), Logger.INFO); return locked; - } // lock(Object) - - + + /** - * @param key - The object who's lock is to be removed, all waiting threads are notified. + * All waiting threads are notified. + * @param key - The object whose lock is to be removed. */ - public void unlock( Object key ) + public synchronized void unlock( Object key ) { // notify all waiting threads if there was a lock on this key if ( this.blocks.remove(key) != null ) { - System.err.println( "Releasing lock on: " + key ); - - // synchronization is required to be an owner of this objects monitor - synchronized( this ) { - notifyAll(); - } + if (LOG) logger.log(this, "Releasing lock on: " + key, Logger.INFO); + notifyAll(); } } // unlock(Object) - + } // inner class Block } // class Engine 1.1 xml-cocoon/src/org/apache/cocoon/Profiler.java Index: Profiler.java =================================================================== /*-- $Id: Profiler.java,v 1.1 2000/09/16 16:04:30 greenrd Exp $ -- ============================================================================ The Apache Software License, Version 1.1 ============================================================================ Copyright (C) @year@ The Apache Software Foundation. All rights reserved. Redistribution and use in source and binary forms, with or without modifica- tion, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by the Apache Software Foundation (http://www.apache.org/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "Cocoon" and "Apache Software Foundation" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact apache@apache.org. 5. Products derived from this software may not be called "Apache", nor may "Apache" appear in their name, without prior written permission of the Apache Software Foundation. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation and was originally created by Stefano Mazzocchi . For more information on the Apache Software Foundation, please see . */ package org.apache.cocoon; import org.apache.cocoon.framework.Actor; import org.apache.cocoon.framework.Director; import org.apache.cocoon.logger.Logger; import org.apache.tools.DOMWriter; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.text.DateFormat; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.servlet.http.HttpServletRequest; /** * Records how long certain processes takes for each request. * * startEvent () and finishEvent () should be called at the start and * end of each process. Use getProfileTable to get the results as a DOM. * * @author Robin Green * @version $Revision: 1.1 $ $Date: 2000/09/16 16:04:30 $ */ public class Profiler implements Actor { public static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance (); protected Logger logger; /** This should be a ListMap. */ protected Hashtable requests = new Hashtable (); protected Vector requestOrder = new Vector (); /** This should be a ListSet. */ protected Vector eventNames = new Vector (); public void init (Director director) { logger = (Logger) director.getActor ("logger"); } public void startEvent (RequestMarker request, Class event) { startEvent (request, mangleName (event.getName ())); } public void startEvent (RequestMarker request, String event) { if (!eventNames.contains (event)) eventNames.addElement (event); Hashtable rm = getRequestMap (request); rm.put (event, new ProfProcess (System.currentTimeMillis (), event)); } public void finishEvent (RequestMarker request, Class event) { finishEvent (request, mangleName (event.getName ())); } public void finishEvent (RequestMarker request, String event) { long finTime = System.currentTimeMillis (); Hashtable rm = getRequestMap (request); ProfProcess pp = (ProfProcess) rm.get (event); if (pp == null) { logger.log (this, "Event '" + event + "' not started yet!", Logger.WARNING); } else { pp.finishedAt (finTime); } } public String mangleName (String name) { return name.replace ('.', '-'); } public void clear () { requests.clear (); requestOrder.removeAllElements (); eventNames.removeAllElements (); } public Element getProfileTable (Document dest) { DOMWriter domWriter = new DOMWriter (dest, "profile-table"); domWriter.push ("headings"); domWriter.addQuick ("heading", "URI"); domWriter.addQuick ("heading", "Date and Time"); for (Enumeration enum = eventNames.elements (); enum.hasMoreElements (); ) { domWriter.addQuick ("heading", (String) enum.nextElement ()); } domWriter.pop ("headings"); for (Enumeration rq = requestOrder.elements (); rq.hasMoreElements (); ) { domWriter.push ("row"); RequestMarker request = (RequestMarker) rq.nextElement (); Hashtable map = (Hashtable) requests.get (request); domWriter.addQuick ("uri", request.uri); domWriter.addQuick ("date-time", DATE_FORMAT.format (request.startTime)); for (Enumeration proc = eventNames.elements (); proc.hasMoreElements (); ) { String event = (String) proc.nextElement (); ProfProcess pp = (ProfProcess) map.get (event); if (pp != null && pp.finished) { domWriter.addQuick (event, "" + pp.getDuration ()); } else { domWriter.addQuick (event, "-"); } } domWriter.pop ("row"); } return (Element) domWriter.getCurrent (); } protected Hashtable getRequestMap (RequestMarker request) { Hashtable map = (Hashtable) requests.get (request); if (map == null) { requests.put (request, map = new Hashtable ()); requestOrder.addElement (request); } return map; } /** * We cannot guarantee that the request object will not be recycled by the * servlet runner, so use RequestMarker instead. */ public static class RequestMarker { protected String uri; protected Date startTime; public RequestMarker (HttpServletRequest req) { // Not safe to store the req object as it might be recycled uri = req.getRequestURI (); startTime = new Date (System.currentTimeMillis ()); } } protected class ProfProcess { protected final String event; protected boolean finished = false; protected long time; protected ProfProcess (long startTime, String event) { this.event = event; time = startTime; } protected void finishedAt (long finishTime) { time = finishTime - time; finished = true; } protected long getDuration () { if (!finished) { logger.log (Profiler.this, "Event '" + event + "' not finished yet!", Logger.WARNING); return -1; } return time; } } } 1.1 xml-cocoon/src/org/apache/tools/DOMWriter.java Index: DOMWriter.java =================================================================== /*-- $Id: DOMWriter.java,v 1.1 2000/09/16 16:04:30 greenrd Exp $ -- ============================================================================ The Apache Software License, Version 1.1 ============================================================================ Copyright (C) @year@ The Apache Software Foundation. All rights reserved. Redistribution and use in source and binary forms, with or without modifica- tion, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by the Apache Software Foundation (http://www.apache.org/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "Cocoon" and "Apache Software Foundation" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact apache@apache.org. 5. Products derived from this software may not be called "Apache", nor may "Apache" appear in their name, without prior written permission of the Apache Software Foundation. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation and was originally created by Stefano Mazzocchi . For more information on the Apache Software Foundation, please see . */ package org.apache.tools; import org.w3c.dom.Document; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * Utility class for creating a DOM tree. * * Use getCurrent() to get the result at the end (make sure to pop() the right * number of times!) * * @author Robin Green * @version $Revision: 1.1 $ $Date: 2000/09/16 16:04:30 $ */ public class DOMWriter { protected final Document doc; protected Node current; public DOMWriter (Document doc) { this.doc = doc; current = doc.createDocumentFragment (); } public DOMWriter (Document doc, String initElement) { this.doc = doc; current = doc.createElement (initElement); } public Element add (String name) throws DOMException { Element e = doc.createElement (name); appendChild (e); return e; } public Element add (String namespaceURI, String name) throws DOMException { Element e = doc.createElementNS (namespaceURI, name); appendChild (e); return e; } public void appendChild (Node n) throws DOMException { current.appendChild (n); } public void setAttribute (String name, String value) throws DOMException { ((Element) current).setAttribute (name, value); } public Document getDocument () { return doc; } public void addText (String text) throws DOMException { current.appendChild (doc.createTextNode (text)); } public Element push (String elemName) throws DOMException { return (Element) (current = add (elemName)); } public Element push (String namespaceURI, String elemName) throws DOMException { return (Element) (current = add (namespaceURI, elemName)); } public Node pop () throws DOMException { Node old = current; current = old.getParentNode (); return old; } public Node pop (String elemName) throws DOMException { Element old = (Element) pop (); String oldName = old.getTagName (); if (!oldName.equals (elemName)) throw new DOMException (DOMException.HIERARCHY_REQUEST_ERR, "Expected '" + elemName + "', but node to be popped is '" + oldName); return old; } public Element addQuick (String name, String contents) throws DOMException { Element e = push (name); addText (contents); pop (); return e; } public Element addQuick (String namespaceURI, String name, String contents) throws DOMException { Element e = push (namespaceURI, name); addText (contents); pop (); return e; } public Node getCurrent () { return current; } public void setCurrent (Node n) { current = n; } } ---------------------------------------------------------------------- In case of troubles, e-mail: webmaster@xml.apache.org To unsubscribe, e-mail: cocoon-cvs-unsubscribe@xml.apache.org For additional commands, e-mail: cocoon-cvs-help@xml.apache.org