Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » generation » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.cocoon.generation;
   18   
   19   import org.apache.commons.collections.ArrayStack;
   20   import org.apache.avalon.framework.component.ComponentException;
   21   import org.apache.avalon.framework.component.ComponentManager;
   22   import org.apache.avalon.framework.configuration.Configurable;
   23   import org.apache.avalon.framework.configuration.Configuration;
   24   import org.apache.avalon.framework.configuration.ConfigurationException;
   25   import org.apache.avalon.framework.parameters.Parameters;
   26   import org.apache.cocoon.ProcessingException;
   27   import org.apache.cocoon.ResourceNotFoundException;
   28   import org.apache.cocoon.caching.CacheableProcessingComponent;
   29   import org.apache.cocoon.components.language.generator.ProgramGenerator;
   30   import org.apache.cocoon.components.source.SourceUtil;
   31   import org.apache.cocoon.environment.SourceResolver;
   32   import org.apache.cocoon.xml.AbstractXMLPipe;
   33   import org.apache.excalibur.source.Source;
   34   import org.apache.excalibur.source.SourceException;
   35   import org.apache.excalibur.source.SourceValidity;
   36   import org.xml.sax.Attributes;
   37   import org.xml.sax.SAXException;
   38   
   39   import java.io.IOException;
   40   import java.io.Serializable;
   41   import java.util.Map;
   42   
   43   /**
   44    * This class acts as a proxy to a dynamically loaded<code>Generator</code>
   45    * delegating actual SAX event generation.
   46    * <p>
   47    * It has a single configuration item :
   48    * <code>&lt;autocomplete-documents&gttrue|false&lt;autocomplete-documents&gt;</code>
   49    * (default is <code>false</code>).
   50    * <p>
   51    * This tells the generator to automatically close all elements that weren't properly closed
   52    * by the XSP, such as when a <code>return</code> statement is used to prematurely end
   53    * processing. Activating this feature <em>sensibly increases CPU-usage</em> and should
   54    * therefore be used only if really needed (it's better to have clean XSP pages that don't
   55    * break abruptly generation flow).
   56    *
   57    * @author <a href="mailto:ricardo@apache.org">Ricardo Rocha</a>
   58    * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
   59    * @version $Id: ServerPagesGenerator.java 433543 2006-08-22 06:22:54Z crossley $
   60    */
   61   public class ServerPagesGenerator extends ServletGenerator
   62           implements CacheableProcessingComponent, Configurable {
   63       /**
   64        * The sitemap-defined server pages program generator
   65        */
   66       protected ProgramGenerator programGenerator = null;
   67   
   68       protected AbstractServerPage generator = null;
   69   
   70       /** The source */
   71       private Source inputSource;
   72   
   73       private CompletionPipe completionPipe;
   74   
   75       /**
   76        * Set the global component manager. This method sets the sitemap-defined
   77        * program generator
   78        *
   79        * @param manager The global component manager
   80        */
   81       public void compose(ComponentManager manager) throws ComponentException {
   82           super.compose(manager);
   83   
   84           if (programGenerator == null) {
   85               this.programGenerator =
   86                       (ProgramGenerator) manager.lookup(ProgramGenerator.ROLE);
   87           }
   88       }
   89   
   90       public void configure(Configuration config) throws ConfigurationException {
   91           boolean autoComplete = config.getChild("autocomplete-documents").getValueAsBoolean(false);
   92   
   93           if (autoComplete) {
   94               this.completionPipe = new CompletionPipe();
   95               this.completionPipe.enableLogging(getLogger());
   96           }
   97   
   98           this.markupLanguage = config.getChild("markup-language").getValue(DEFAULT_MARKUP_LANGUAGE);
   99           this.programmingLanguage = config.getChild("programming-language").getValue(DEFAULT_PROGRAMMING_LANGUAGE);
  100       }
  101   
  102       /**
  103        * Generate the unique key.
  104        * This key must be unique inside the space of this component.
  105        * This method must be invoked before the generateValidity() method.
  106        *
  107        * @return The generated key or <code>null</code> if the component
  108        *         is currently not cacheable.
  109        */
  110       public Serializable getKey() {
  111           Object key = generator.getKey();
  112           if (key == null) {
  113               return this.inputSource.getURI();
  114           }
  115           return this.inputSource.getURI() + '-' + key;
  116       }
  117   
  118       /**
  119        * Generate the validity object.
  120        * Before this method can be invoked the generateKey() method
  121        * must be invoked.
  122        *
  123        * @return The generated validity object or <code>null</code> if the
  124        *         component is currently not cacheable.
  125        */
  126       public SourceValidity getValidity() {
  127           // VG: Input source's systemID is part of the key,
  128           // and need not be included into the validity.
  129           return generator.getValidity();
  130       }
  131   
  132       /**
  133        * The loaded generator's <code>MarkupLanguage</code>
  134        */
  135       protected String markupLanguage;
  136   
  137       /**
  138        * The loaded generator's <code>ProgrammingLanguage</code>
  139        */
  140       protected String programmingLanguage;
  141   
  142       /**
  143        * The default <code>MarkupLanguage</code>
  144        */
  145       public final static String DEFAULT_MARKUP_LANGUAGE = "xsp";
  146   
  147       /**
  148        * The default <code>ProgrammingLanguage</code>
  149        */
  150       public final static String DEFAULT_PROGRAMMING_LANGUAGE = "java";
  151   
  152       public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par)
  153               throws ProcessingException, SAXException, IOException {
  154   
  155           super.setup(resolver, objectModel, src, par);
  156   
  157           String markupLanguage = this.parameters.getParameter(
  158                   "markup-language", this.markupLanguage);
  159           String programmingLanguage = this.parameters.getParameter(
  160                   "programming-language", this.programmingLanguage);
  161   
  162           try {
  163               this.inputSource = this.resolver.resolveURI(src);
  164           } catch (SourceException se) {
  165               throw SourceUtil.handle("Error during resolving of '" + src + "'.", se);
  166               //throw SourceUtil.handle(se);
  167           }
  168   
  169           try {
  170               this.generator = (AbstractServerPage) programGenerator.load(this.manager,
  171                       this.inputSource, markupLanguage, programmingLanguage, this.resolver);
  172           } catch (ProcessingException e) {
  173               throw e;
  174           } catch (Exception e) {
  175               getLogger().warn("setup()", e);
  176               throw new ProcessingException(e.getMessage(), e);
  177           } catch (NoClassDefFoundError e) {
  178               // VG: Usually indicates that page invoked with the wrong case.
  179               // I.e., it was compiled as "my.xsp" and inoked as "My.xsp",
  180               // results in different class name and an error.
  181               getLogger().warn("Failed to load class: " + e);
  182               throw new ResourceNotFoundException(e.getMessage());
  183           }
  184   
  185           // Give our own logger to the generator so that logs go in the correct category
  186           generator.enableLogging(getLogger());
  187   
  188           generator.setup(super.resolver, super.objectModel, super.source, super.parameters);
  189       }
  190   
  191       /**
  192        * Generate XML data. This method loads a server pages generator associated
  193        * with its (file) input source and delegates SAX event generator to it
  194        * taking care of "closing" any event left open by the loaded generator as a
  195        * result of its possible "premature" return (a common situation in server
  196        * pages)
  197        *
  198        * @exception IOException IO Error
  199        * @exception SAXException SAX event generation error
  200        * @exception ProcessingException Error during load/execution
  201        */
  202       public void generate() throws IOException, SAXException, ProcessingException {
  203   
  204           if (this.completionPipe != null) {
  205               generator.setConsumer(this.completionPipe);
  206               if (this.xmlConsumer != null) {
  207                   this.completionPipe.setConsumer(this.xmlConsumer);
  208               } else {
  209                   this.completionPipe.setContentHandler(this.contentHandler);
  210                   this.completionPipe.setLexicalHandler(this.lexicalHandler);
  211               }
  212           } else {
  213               if (this.xmlConsumer != null) {
  214                   generator.setConsumer(this.xmlConsumer);
  215               } else {
  216                   generator.setContentHandler(this.contentHandler);
  217                   generator.setLexicalHandler(this.lexicalHandler);
  218               }
  219           }
  220   
  221           // Fixes BUG#4062: Set document locator which is used by XIncludeTransformer
  222           org.xml.sax.helpers.LocatorImpl locator = new org.xml.sax.helpers.LocatorImpl();
  223           locator.setSystemId(this.inputSource.getURI());
  224           this.contentHandler.setDocumentLocator(locator);
  225   
  226           // Log exception and ensure that generator is released.
  227           try {
  228               generator.generate();
  229           } catch (IOException e) {
  230               getLogger().debug("IOException in generate()", e);
  231               throw e;
  232           } catch (SAXException e) {
  233               getLogger().debug("SAXException in generate()", e);
  234               throw e;
  235           } catch (ProcessingException e) {
  236               getLogger().debug("ProcessingException in generate()", e);
  237               throw e;
  238           } catch (Exception e) {
  239               getLogger().debug("Exception in generate()", e);
  240               throw new ProcessingException("Exception in ServerPagesGenerator.generate()", e);
  241           } finally {
  242               if (generator != null) {
  243                   programGenerator.release(generator);
  244               }
  245               generator = null;
  246           }
  247   
  248           if (this.completionPipe != null) {
  249               this.completionPipe.flushEvents();
  250           }
  251       }
  252   
  253       /**
  254        * Recycle the generator by removing references
  255        */
  256       public void recycle() {
  257           if (this.generator != null) {
  258               this.programGenerator.release(this.generator);
  259               this.generator = null;
  260           }
  261           if (this.inputSource != null) {
  262               this.resolver.release(this.inputSource);
  263               this.inputSource = null;
  264           }
  265           if (this.completionPipe != null) {
  266               this.completionPipe.recycle();
  267               this.completionPipe = null;
  268           }
  269           super.recycle();
  270       }
  271   
  272       /**
  273        * dispose
  274        */
  275       public void dispose() {
  276           this.manager.release(this.programGenerator);
  277           this.programGenerator = null;
  278           this.manager = null;
  279       }
  280   
  281       /* Completion pipe */
  282   
  283       // int values for event types
  284       private final static int DOCUMENT = 0;
  285       private final static int ELEMENT = 1;
  286       private final static int PREFIX_MAPPING = 2;
  287       private final static int CDATA = 3;
  288       private final static int DTD = 4;
  289       private final static int ENTITY = 5;
  290   
  291       // Integer equivalents to push on the stack
  292       private final static Integer DOCUMENT_OBJ = new Integer(DOCUMENT);
  293       private final static Integer ELEMENT_OBJ = new Integer(ELEMENT);
  294       private final static Integer PREFIX_MAPPING_OBJ = new Integer(PREFIX_MAPPING);
  295       private final static Integer CDATA_OBJ = new Integer(CDATA);
  296       private final static Integer DTD_OBJ = new Integer(DTD);
  297       private final static Integer ENTITY_OBJ = new Integer(ENTITY);
  298   
  299       public class CompletionPipe extends AbstractXMLPipe {
  300   
  301           /**
  302            * The SAX event stack. Used for "completing" pendind SAX events left "open"
  303            * by prematurely returning server pages generators
  304            */
  305           protected ArrayStack eventStack = new ArrayStack();
  306   
  307           /**
  308            * Receive notification of the beginning of a document.
  309            */
  310           public void startDocument() throws SAXException {
  311               super.startDocument();
  312               this.eventStack.push(DOCUMENT_OBJ);
  313           }
  314   
  315           /**
  316            * Receive notification of the end of a document.
  317            */
  318           public void endDocument() throws SAXException {
  319               this.eventStack.pop();
  320               super.endDocument();
  321           }
  322   
  323           /**
  324            * Receive notification of the beginning of an element.
  325            */
  326           public void startElement(String namespaceURI, String localName, String rawName, Attributes atts)
  327                   throws SAXException {
  328               super.startElement(namespaceURI, localName, rawName, atts);
  329               this.eventStack.push(rawName);
  330               this.eventStack.push(localName);
  331               this.eventStack.push(namespaceURI);
  332               this.eventStack.push(ELEMENT_OBJ);
  333           }
  334   
  335           /**
  336            * Receive notification of the end of an element.
  337            */
  338           public void endElement(String namespaceURI, String localName, String rawName)
  339                   throws SAXException {
  340               this.eventStack.pop(); // ELEMENT_OBJ
  341               this.eventStack.pop(); // namespaceURI
  342               this.eventStack.pop(); // localName
  343               this.eventStack.pop(); // rawName
  344               super.endElement(namespaceURI, localName, rawName);
  345           }
  346   
  347           /**
  348            * Begin the scope of a prefix-URI Namespace mapping.
  349            */
  350           public void startPrefixMapping(String prefix, String uri) throws SAXException {
  351               super.startPrefixMapping(prefix, uri);
  352               this.eventStack.push(prefix);
  353               this.eventStack.push(PREFIX_MAPPING_OBJ);
  354           }
  355   
  356           /**
  357            * End the scope of a prefix-URI mapping.
  358            */
  359           public void endPrefixMapping(String prefix) throws SAXException {
  360               this.eventStack.pop(); // PREFIX_MAPPING_OBJ
  361               this.eventStack.pop(); // prefix
  362               super.endPrefixMapping(prefix);
  363           }
  364   
  365           public void startCDATA() throws SAXException {
  366               super.startCDATA();
  367               this.eventStack.push(CDATA_OBJ);
  368           }
  369   
  370           public void endCDATA() throws SAXException {
  371               this.eventStack.pop();
  372               super.endCDATA();
  373           }
  374   
  375           public void startDTD(String name, String publicId, String systemId)
  376                   throws SAXException {
  377               super.startDTD(name, publicId, systemId);
  378               this.eventStack.push(DTD_OBJ);
  379           }
  380   
  381           public void endDTD() throws SAXException {
  382               this.eventStack.pop();
  383               super.endDTD();
  384           }
  385   
  386           public void startEntity(String name) throws SAXException {
  387               super.startEntity(name);
  388               this.eventStack.push(name);
  389               this.eventStack.push(ENTITY_OBJ);
  390           }
  391   
  392           public void endEntity(String name) throws SAXException {
  393               this.eventStack.pop(); // ENTITY_OBJ
  394               this.eventStack.pop(); // name
  395               super.endEntity(name);
  396           }
  397   
  398           public void flushEvents() throws SAXException {
  399   
  400               if (this.getLogger().isWarnEnabled()) {
  401                   if (this.eventStack.size() > 0) {
  402                       this.getLogger().warn("Premature end of document generated by " + inputSource.getURI());
  403                   }
  404               }
  405   
  406               // End any started events in case of premature return
  407               while (this.eventStack.size() != 0) {
  408                   int event = ((Integer) eventStack.pop()).intValue();
  409   
  410                   switch (event) {
  411                       case DOCUMENT:
  412                           super.endDocument();
  413                           break;
  414   
  415                       case ELEMENT:
  416                           String namespaceURI = (String) eventStack.pop();
  417                           String localName = (String) eventStack.pop();
  418                           String rawName = (String) eventStack.pop();
  419                           super.endElement(namespaceURI, localName, rawName);
  420                           break;
  421   
  422                       case PREFIX_MAPPING:
  423                           super.endPrefixMapping((String) eventStack.pop());
  424                           break;
  425   
  426                       case CDATA:
  427                           super.endCDATA();
  428                           break;
  429   
  430                       case DTD:
  431                           super.endDTD();
  432                           break;
  433   
  434                       case ENTITY:
  435                           super.endEntity((String) eventStack.pop());
  436                           break;
  437                   }
  438               }
  439           }
  440   
  441           public void recycle() {
  442               this.eventStack.clear();
  443               super.recycle();
  444           }
  445       }
  446   }

Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » generation » [javadoc | source]