You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by gr...@locus.apache.org on 2000/09/16 18:04:31 UTC

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 @@
   
   <!--
     History of Cocoon changes
  -  $Id: changes.xml,v 1.108 2000/09/15 04:38:13 balld Exp $
  +  $Id: changes.xml,v 1.109 2000/09/16 16:04:28 greenrd Exp $
   -->
   
   <changes title="History of Changes">
  @@ -13,14 +13,23 @@
       <person name="Donald Ball" email="balld@webslingerZ.com" id="DB"/>
       <person name="Ricardo Rocha" email="ricardo@apache.org" id="RR"/>
       <person name="Pierpaolo Fumagalli" email="pier@apache.org" id="PF"/>
  +    <person name="Robin Green" email="greenrd@hotmail.com" id="RDG"/>
     </devs>
   
    <release version="@version@" date="@date@">
  +  <action dev="RDG" type="add">
  +   Added very primitive profiler (see cocoon.properties)
  +  </action>
  +  <action dev="RDG" type="fix">
  +   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.)
  +  </action>
     <action dev="DB" type="add">
  -   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?
     </action>
     <action dev="DB" type="add">
  -   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.
     </action>
     <action dev="DB" type="fix" due-to="Tagunov Anthony" due-to-email="atagunov@nnt.ru">
      Fixed encoding problem with xinclude processor
  @@ -28,11 +37,15 @@
     <action dev="SM" type="fix" due-to="Kevin Sonney" due-to-email="kevin@webslingerz.com">
      Fixed problem with XSP and namespaces (now follows the correct name="xml-stylesheet" syntax)
     </action>
  +  <action dev="SM" type="update">
  +   Upgraded Xerces to 1.2 because previous version had a bug which meant it couldn't build
  +   the Cocoon documentation.
  +  </action>
     <action dev="DB" type="add">
      Added esql logicsheet
     </action>
  -  <action dev="DB" type="add">
  -   Upgraded xalan to 1_2_D01
  +  <action dev="DB" type="update">
  +   Upgraded xalan to 1_2_D02
     </action>
     <action dev="SM" type="add" due-to="Paul Terray" due-to-email="terray@4dconcept.fr">
      Added installation instructions for iPlanet.
  @@ -53,7 +66,7 @@
      Patched the cookie XSP taglib and the LDAP processor.
     </action>
     <action dev="SM" type="fix" due-to="Mark Washeim" due-to-email="esalon@canuck.com">
  -   Make Cocoon running better under heavy load.
  +   Make Cocoon run better under heavy load.
     </action>
     <action dev="SM" type="add" due-to="Mark Evans" due-to-email="mark.evans@dsto.defence.gov.au">
      Added Solaris8 and improved Win2k installation case documentation.
  
  
  
  1.1                  xml-cocoon/samples/profiler/profiler.xml
  
  Index: profiler.xml
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet type="text/xsl" href="profiler.xsl"?>
  <?cocoon-process type="xsp"?>
  <?cocoon-process type="xslt"?>
  
  <xsp:page language="java" xmlns:xsp="http://www.apache.org/1999/XSP/Core">
  
    <xsp:structure>
      <xsp:include>org.apache.cocoon.Profiler</xsp:include>
    </xsp:structure>
  
    <xsp:logic>
  
      private Profiler profiler;
  
      public void init (Director director) {
        super.init (director);
        profiler = (Profiler) director.getActor ("profiler");
      }
    </xsp:logic>
  
    <profile>
      <xsp:logic>
        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.");
        }
      </xsp:logic>
      <xsp:expr>profiler.getProfileTable (document)</xsp:expr>
    </profile>
  </xsp:page>
  
  
  1.1                  xml-cocoon/samples/profiler/profiler.xsl
  
  Index: profiler.xsl
  ===================================================================
  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
    <xsl:template match="profile">
      <html>
        <head>
          <title>Profiler Table</title>
        </head>
        <body>
          <xsl:apply-templates/>
        </body>
      </html>
    </xsl:template>
  
    <xsl:template match="profile-table">
      <table border="1">
        <xsl:apply-templates/>
      </table>
    </xsl:template>
  
    <xsl:template match="headings|row">
      <tr>
        <xsl:apply-templates/>
      </tr>
    </xsl:template>
  
    <xsl:template match="heading">
      <th><xsl:value-of select="."/></th>
    </xsl:template>
  
    <xsl:template match="*" priority="-1">
      <td><xsl:value-of select="."/></td>
    </xsl:template>
  </xsl:stylesheet>
  
  
  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 <?xml?> declaration
  -#   - The XSLT stylesheet <xsl:output> "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 <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
  - * @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 <http://www.apache.org/>.
   
    */
  - 
  +
   package org.apache.cocoon;
   
   import java.io.*;
  @@ -74,14 +74,16 @@
    * This class implements the engine that does all the document processing.
    *
    * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
  - * @version $Revision: 1.33 $ $Date: 2000/08/17 22:34:13 $
  + * @author <a href="mailto:greenrd@hotmail.com">Robin Green</a>
  + * @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("<!-- free memory: " + Runtime.getRuntime().freeMemory() + " -->");
                   }
  -    
  +
                   // 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  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
   */
  
  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 <a href="mailto:greenrd@hotmail.com">Robin Green</a>
   * @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  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
  
   */
  
  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 <a href="mailto:greenrd@hotmail.com">Robin Green</a>
   * @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;
    }
  }