Source Code Cross Referenced for IncludeTransformer.java in  » Content-Management-System » apache-lenya-2.0 » org » apache » cocoon » transformation » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Content Management System » apache lenya 2.0 » org.apache.cocoon.transformation 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         *
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:        package org.apache.cocoon.transformation;
0018:
0019:        import org.apache.avalon.framework.CascadingRuntimeException;
0020:        import org.apache.avalon.framework.component.WrapperComponentManager;
0021:        import org.apache.avalon.framework.configuration.Configurable;
0022:        import org.apache.avalon.framework.configuration.Configuration;
0023:        import org.apache.avalon.framework.configuration.ConfigurationException;
0024:        import org.apache.avalon.framework.logger.AbstractLogEnabled;
0025:        import org.apache.avalon.framework.logger.Logger;
0026:        import org.apache.avalon.framework.parameters.Parameters;
0027:        import org.apache.avalon.framework.service.ServiceException;
0028:        import org.apache.avalon.framework.service.ServiceManager;
0029:        import org.apache.avalon.framework.service.Serviceable;
0030:
0031:        import org.apache.cocoon.ProcessingException;
0032:        import org.apache.cocoon.Processor;
0033:        import org.apache.cocoon.caching.CacheableProcessingComponent;
0034:        import org.apache.cocoon.components.CocoonComponentManager;
0035:        import org.apache.cocoon.components.source.SourceUtil;
0036:        import org.apache.cocoon.components.source.impl.MultiSourceValidity;
0037:        import org.apache.cocoon.components.thread.RunnableManager;
0038:        import org.apache.cocoon.environment.Environment;
0039:        import org.apache.cocoon.environment.SourceResolver;
0040:        import org.apache.cocoon.transformation.helpers.NOPRecorder;
0041:        import org.apache.cocoon.util.NetUtils;
0042:        import org.apache.cocoon.xml.AbstractXMLPipe;
0043:        import org.apache.cocoon.xml.IncludeXMLConsumer;
0044:        import org.apache.cocoon.xml.NamespacesTable;
0045:        import org.apache.cocoon.xml.SaxBuffer;
0046:        import org.apache.cocoon.xml.XMLConsumer;
0047:
0048:        import org.apache.excalibur.source.Source;
0049:        import org.apache.excalibur.source.SourceValidity;
0050:        import org.xml.sax.Attributes;
0051:        import org.xml.sax.ContentHandler;
0052:        import org.xml.sax.Locator;
0053:        import org.xml.sax.SAXException;
0054:        import org.xml.sax.ext.LexicalHandler;
0055:
0056:        import java.io.IOException;
0057:        import java.io.Serializable;
0058:        import java.io.UnsupportedEncodingException;
0059:        import java.util.HashMap;
0060:        import java.util.Map;
0061:        import java.util.Stack;
0062:
0063:        /**
0064:         * <p>A simple transformer including resolvable sources (accessed through
0065:         * Cocoon's {@link SourceResolver}) from its input.</p>
0066:         *
0067:         * <p>Inclusion is triggered by the <code>&lt;include ... /&gt;</code> element
0068:         * defined in the <code>http://apache.org/cocoon/include/1.0</code> namespace.</p>
0069:         *
0070:         * <p>Example:</p>
0071:         * <pre>
0072:         * &lt;i:include xmlns:i="http://apache.org/cocoon/include/1.0"
0073:         *               src="cocoon://path/to/include"/&gt;
0074:         * </pre>
0075:         *
0076:         * <p>An interesting feature of this {@link Transformer} is that it implements the
0077:         * {@link CacheableProcessingComponent} interface and provides full support for
0078:         * caching. In other words, if the input given to this transformer has not changed,
0079:         * and all of the included sources are (cacheable) and still valid, this transformer
0080:         * will not force a pipeline re-generation like the {@link CIncludeTransformer}.</p>
0081:         *
0082:         *
0083:         * <h3>Relative Source Resolution</h3>
0084:         * <p>Include sources which are specified using relative URI will be resolved
0085:         * relative to the source document location. This is consistent with
0086:         * {@link XIncludeTransformer} behavior, but differs from {@link CIncludeTransformer}.
0087:         *
0088:         *
0089:         * <h3>Parameters Passing</h3>
0090:         * <p>Parameters to be passed to the included sources can be specified in two ways:
0091:         * the first one is to encode them onto the source itelf, for example:</p>
0092:         *
0093:         * <pre>
0094:         * &lt;i:include xmlns:i="http://apache.org/cocoon/include/1.0"
0095:         *               src="cocoon://path/to/include?paramA=valueA&amp;paramB=valueB"/&gt;
0096:         * </pre>
0097:         *
0098:         * <p>Another approach allows the encoding of parameters to be done automatically by
0099:         * the transformer, so that one can easily pass parameter name or values containing
0100:         * the <code>&amp;</code> (amperstand) or <code>=</code> (equals) character, which are
0101:         * reserved characters in URIs. An example:</p>
0102:         *
0103:         * <pre>
0104:         * &lt;i:include xmlns:i="http://apache.org/cocoon/include/1.0"
0105:         *               src="cocoon://path/to/include"&gt;
0106:         *   &lt;i:parameter name="firstParameterName" value="firstParameterValue"/&gt;
0107:         *   &lt;i:parameter name="other&amp;Para=Name" value="other=Para&amp;Value"/&gt;
0108:         * &lt;/i:include&gt;
0109:         * </pre>
0110:         *
0111:         *
0112:         * <h3>Fallback Element</h3>
0113:         * <p>IncludeTransformer allows fallback element to be specified within
0114:         * include element. XML content of the fallback element will be included instead
0115:         * of source content if source inclusion caused an exception. Fallback element
0116:         * can have nested include elements. An example:</p>
0117:         *
0118:         * <pre>
0119:         * &lt;i:include xmlns:i="http://apache.org/cocoon/include/1.0"
0120:         *               src="cocoon://path/to/include"&gt;
0121:         *   &lt;i:fallback&gt;
0122:         *     <strong>The data is temporarily unavailable.</strong>
0123:         *     We are sorry for the trouble; please try again later.
0124:         *   &lt;/i:fallback&gt;
0125:         * &lt;/i:include&gt;
0126:         * </pre>
0127:         *
0128:         *
0129:         * <h3>Parallel Processing</h3>
0130:         * <p>Another feature of this {@link Transformer} is that it allows parallel processing
0131:         * of includes. By setting the optional parameter <code>parallel</code> to true,
0132:         * the various included contents are processed (included) in parallel threads rather
0133:         * than in series, in one thread. This parameter can be set in either the transformer
0134:         * definition (to affect all IncludeTransformer instances):</p>
0135:         * <pre>
0136:         *   &lt;parallel&gt;true&lt;/parallel&gt;
0137:         * </pre>
0138:         *
0139:         * <p>or in a pipeline itself (to only affect that instance of the IncludeTransformer):</p>
0140:         * <pre>
0141:         *   &lt;map:parameter name="parallel" value="true"/&gt;
0142:         * </pre>
0143:         * <p>By default, parallel processing is turned off.</p>
0144:         *
0145:         *
0146:         * <h3>Recursive Processing</h3>
0147:         * <p>This {@link Transformer} allows recursive processing of includes.
0148:         * By setting the optional parameter <code>recursive</code> to true,
0149:         * the various included contents are scanned for include elements, and processed
0150:         * in the same manner as incoming XML events. This parameter can be set in either
0151:         * the transformer definition (to affect all IncludeTransformer instances):</p>
0152:         * <pre>
0153:         *   &lt;recursive&gt;true&lt;/recursive&gt;
0154:         * </pre>
0155:         *
0156:         * <p>or in a pipeline itself (to only affect that instance of the IncludeTransformer):</p>
0157:         * <pre>
0158:         *   &lt;map:parameter name="recursive" value="true"/&gt;
0159:         * </pre>
0160:         * <p>This feature is similar to the XInclude processing. By default,
0161:         * recursive processing is turned off.</p>
0162:         *
0163:         *
0164:         * @cocoon.sitemap.component.documentation
0165:         * A simple transformer including resolvable sources (accessed through
0166:         * Cocoon's SourceResolver) from its input.
0167:         *
0168:         * @cocoon.sitemap.component.name include
0169:         * @cocoon.sitemap.component.logger sitemap.transformer.include
0170:         * @cocoon.sitemap.component.pooling.max 16
0171:         * @version $Id: IncludeTransformer.java 433543 2006-08-22 06:22:54Z crossley $
0172:         */
0173:        public class IncludeTransformer extends AbstractTransformer implements 
0174:                Serviceable, Configurable, CacheableProcessingComponent {
0175:
0176:            /** <p>The namespace URI of the elements recognized by this transformer.</p> */
0177:            private static final String NS_URI = "http://apache.org/cocoon/include/1.0";
0178:
0179:            /** <p>The name of the element triggering inclusion of sources.</p> */
0180:            private static final String INCLUDE_ELEMENT = "include";
0181:
0182:            /** <p>The name of the element defining a fallback content.</p> */
0183:            private static final String FALLBACK_ELEMENT = "fallback";
0184:
0185:            /** <p>The name of the element defining an included subrequest parameter.</p> */
0186:            private static final String PARAMETER_ELEMENT = "parameter";
0187:
0188:            /** <p>The name of the attribute indicating the included source URI.</p> */
0189:            private static final String SRC_ATTRIBUTE = "src";
0190:
0191:            /** <p>The name of the mime type attribute containing the hint for the {@link org.apache.excalibur.xmlizer.XMLizer}.</p> */
0192:            private static final String MIME_ATTRIBUTE = "mime-type";
0193:
0194:            /** <p>The name of the parse attribute indicating type of included source processing: xml or text.</p> */
0195:            private static final String PARSE_ATTRIBUTE = "parse";
0196:
0197:            /** <p>The name of the attribute indicating the parameter name.</p> */
0198:            private static final String NAME_ATTRIBUTE = "name";
0199:
0200:            /** <p>The name of the attribute indicating the parameter name.</p> */
0201:            private static final String VALUE_ATTRIBUTE = "value";
0202:
0203:            /** <p>The encoding to use for parameter names and values.</p> */
0204:            private static final String ENCODING = "US-ASCII";
0205:
0206:            //
0207:            // Global configuration
0208:            //
0209:
0210:            /** The {@link ServiceManager} instance associated with this instance. */
0211:            protected ServiceManager manager;
0212:
0213:            /** Configuration option controlling recursive includes processing */
0214:            private boolean defaultRecursive;
0215:
0216:            /** Configuration option controlling parallel (in multiple threads) includes processing */
0217:            private boolean defaultParallel;
0218:
0219:            /** Configuration option controlling parallel (in multiple threads) includes processing in the recursive includes */
0220:            private boolean defaultRecursiveParallel;
0221:
0222:            /** The name of the thread pool to use (for parallel processing). */
0223:            protected String threadPool;
0224:
0225:            /** The default value to be appended to the caching key. */
0226:            private String defaultKey;
0227:
0228:            //
0229:            // Current configuration
0230:            //
0231:
0232:            /** The {@link SourceResolver} used to resolve included URIs. */
0233:            protected SourceResolver resolver;
0234:
0235:            /** The {@link Environment} used within parallel threads */
0236:            protected Environment environment;
0237:
0238:            /** The {@link Processor} used within parallel threads */
0239:            private Processor processor;
0240:
0241:            /** The value to be appended to the caching key. */
0242:            private String key;
0243:
0244:            //
0245:            // Current state
0246:            //
0247:
0248:            /** The {@link SourceValidity} instance associated with this request. */
0249:            protected MultiSourceValidity validity;
0250:
0251:            /** A {@link NamespacesTable} used to filter namespace declarations. */
0252:            private NamespacesTable namespaces;
0253:
0254:            /** The {@link IncludeXMLPipe} which is doing all the work */
0255:            private final IncludeXMLPipe pipe;
0256:
0257:            /**
0258:             * <p>Create a new {@link IncludeTransformer} instance.</p>
0259:             */
0260:            public IncludeTransformer() {
0261:                pipe = new IncludeXMLPipe();
0262:            }
0263:
0264:            /**
0265:             * <p>Initialize own and {@link #pipe} loggers</p>
0266:             */
0267:            public void enableLogging(Logger logger) {
0268:                super .enableLogging(logger);
0269:                pipe.enableLogging(logger);
0270:            }
0271:
0272:            /**
0273:             * <p>Setup the {@link ServiceManager} available for this instance.</p>
0274:             *
0275:             * @see Serviceable#service(ServiceManager)
0276:             */
0277:            public void service(ServiceManager manager) throws ServiceException {
0278:                this .manager = manager;
0279:            }
0280:
0281:            /* (non-Javadoc)
0282:             * @see Configurable#configure(Configuration)
0283:             */
0284:            public void configure(Configuration configuration)
0285:                    throws ConfigurationException {
0286:                /* Read configuration nodes for recursive, parallel, recursive-parallel */
0287:                this .defaultRecursive = configuration.getChild("recursive")
0288:                        .getValueAsBoolean(false);
0289:                this .defaultParallel = configuration.getChild("parallel")
0290:                        .getValueAsBoolean(false);
0291:                this .defaultRecursiveParallel = configuration.getChild(
0292:                        "recursive-parallel").getValueAsBoolean(false);
0293:                /* Read configuration node for thread pool name */
0294:                this .threadPool = configuration.getChild("thread-pool")
0295:                        .getValue("default");
0296:                this .defaultKey = configuration.getChild("key").getValue(null);
0297:            }
0298:
0299:            /**
0300:             * <p>Setup this component instance in the context of its pipeline and
0301:             * current request.</p>
0302:             *
0303:             * @see Serviceable#service(ServiceManager)
0304:             */
0305:            public void setup(SourceResolver resolver, Map om, String src,
0306:                    Parameters parameters) throws ProcessingException,
0307:                    SAXException, IOException {
0308:                /* Read sitemap parameters */
0309:                this .pipe.recursive = parameters.getParameterAsBoolean(
0310:                        "recursive", this .defaultRecursive);
0311:                this .pipe.parallel = parameters.getParameterAsBoolean(
0312:                        "parallel", this .defaultParallel);
0313:                this .pipe.recursiveParallel = parameters.getParameterAsBoolean(
0314:                        "recursive-parallel", this .defaultRecursiveParallel);
0315:                this .key = parameters.getParameter("key", this .defaultKey);
0316:
0317:                /* Init transformer state */
0318:                if (this .pipe.parallel) {
0319:                    this .environment = CocoonComponentManager
0320:                            .getCurrentEnvironment();
0321:                    this .processor = CocoonComponentManager
0322:                            .getCurrentProcessor();
0323:                }
0324:                this .namespaces = new NamespacesTable();
0325:                this .resolver = resolver;
0326:                this .validity = null;
0327:
0328:                // Set root include pipe as consumer.
0329:                // Won't use setter methods here - they are overridden
0330:                super .xmlConsumer = pipe;
0331:                super .contentHandler = pipe;
0332:                super .lexicalHandler = pipe;
0333:            }
0334:
0335:            public void setConsumer(XMLConsumer consumer) {
0336:                pipe.setConsumer(consumer);
0337:            }
0338:
0339:            public void setContentHandler(ContentHandler handler) {
0340:                pipe.setContentHandler(handler);
0341:            }
0342:
0343:            public void setLexicalHandler(LexicalHandler handler) {
0344:                pipe.setLexicalHandler(handler);
0345:            }
0346:
0347:            /**
0348:             * <p>Recycle this component instance.</p>
0349:             *
0350:             * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
0351:             */
0352:            public void recycle() {
0353:                this .namespaces = null;
0354:                this .validity = null;
0355:
0356:                /* Make sure all threads completed their work */
0357:                this .pipe.recycle();
0358:
0359:                // Resolver can be nulled out when all threads completed processing
0360:                // and released their Sources.
0361:                this .resolver = null;
0362:
0363:                super .recycle();
0364:            }
0365:
0366:            /**
0367:             * <p>Receive notification of the beginning of an XML document.</p>
0368:             *
0369:             * @see ContentHandler#startDocument
0370:             */
0371:            public void startDocument() throws SAXException {
0372:                /* Make sure that we have a validity while processing */
0373:                getValidity();
0374:
0375:                super .startDocument();
0376:            }
0377:
0378:            /**
0379:             * <p>Receive notification of the end of an XML document.</p>
0380:             *
0381:             * @see ContentHandler#startDocument()
0382:             */
0383:            public void endDocument() throws SAXException {
0384:                /* Make sure that the validity is "closed" at the end */
0385:                this .validity.close();
0386:
0387:                super .endDocument();
0388:            }
0389:
0390:            /**
0391:             * <p>Receive notification of the start of a prefix mapping.</p>
0392:             *
0393:             * <p>This transformer will remove all prefix mapping declarations for those
0394:             * prefixes associated with the <code>http://apache.org/cocoon/include/1.0</code>
0395:             * namespace.</p>
0396:             *
0397:             * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
0398:             */
0399:            public void startPrefixMapping(String prefix, String nsuri)
0400:                    throws SAXException {
0401:                if (NS_URI.equals(nsuri)) {
0402:                    /* Skipping mapping for the current prefix as it's ours */
0403:                    this .namespaces.addDeclaration(prefix, nsuri);
0404:                } else {
0405:                    /* Map the current prefix, as we don't know it */
0406:                    super .startPrefixMapping(prefix, nsuri);
0407:                }
0408:            }
0409:
0410:            /**
0411:             * <p>Receive notification of the end of a prefix mapping.</p>
0412:             *
0413:             * <p>This transformer will remove all prefix mapping declarations for those
0414:             * prefixes associated with the <code>http://apache.org/cocoon/include/1.0</code>
0415:             * namespace.</p>
0416:             *
0417:             * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
0418:             */
0419:            public void endPrefixMapping(String prefix) throws SAXException {
0420:                if (NS_URI.equals(this .namespaces.getUri(prefix))) {
0421:                    /* Skipping unmapping for the current prefix as it's ours */
0422:                    this .namespaces.removeDeclaration(prefix);
0423:                } else {
0424:                    /* Unmap the current prefix, as we don't know it */
0425:                    super .endPrefixMapping(prefix);
0426:                }
0427:            }
0428:
0429:            /**
0430:             * <p>Return the caching key associated with this transformation.</p>
0431:             *
0432:             * <p>When including <code>cocoon://</code> sources with dynamic
0433:             * content depending on environment (request parameters, session attributes,
0434:             * etc), it makes sense to provide such environment values to the transformer
0435:             * to be included into the key using <code>key</code> sitemap parameter.</p>
0436:             *
0437:             * @see CacheableProcessingComponent#getKey()
0438:             */
0439:            public Serializable getKey() {
0440:                /*
0441:                 * In case of including "cocoon://" or other dynamic sources key
0442:                 * ideally has to include ProcessingPipelineKey of the included
0443:                 * "cocoon://" sources, but it's not possible as at this time
0444:                 * we don't know yet which sources will get included into the
0445:                 * response.
0446:                 *
0447:                 * Hence, javadoc recommends providing key using sitemap parameter.
0448:                 */
0449:                return key == null ? "I" : "I" + key;
0450:            }
0451:
0452:            /**
0453:             * <p>Generate (or return) the {@link SourceValidity} instance used to
0454:             * possibly validate cached generations.</p>
0455:             *
0456:             * @return a <b>non null</b> {@link SourceValidity}.
0457:             * @see org.apache.cocoon.caching.CacheableProcessingComponent#getValidity()
0458:             */
0459:            public SourceValidity getValidity() {
0460:                if (validity == null) {
0461:                    validity = new MultiSourceValidity(resolver, -1);
0462:                }
0463:                return validity;
0464:            }
0465:
0466:            /**
0467:             * Description of the include element
0468:             */
0469:            private class IncludeElement extends AbstractLogEnabled {
0470:                /** Parameter controlling recursive includes processing */
0471:                private boolean recursive;
0472:
0473:                /** Parameter controlling parallel (in multiple threads) includes processing */
0474:                private boolean parallel;
0475:
0476:                /** Parameter controlling parallel (in multiple threads) includes processing in recursive includes */
0477:                private boolean recursiveParallel;
0478:
0479:                /** The source base URI. */
0480:                private String base;
0481:
0482:                /** The source URI to be included declared in an src attribute of the include element. */
0483:                protected String source;
0484:
0485:                /** The flag indicating whether source content has to be parsed into XML or included as text. */
0486:                protected boolean parse;
0487:
0488:                /** The mime type hint to the {@link org.apache.excalibur.xmlizer.XMLizer} when parsing the source content. */
0489:                protected String mimeType;
0490:
0491:                /** The buffer collecting fallback content. */
0492:                protected SaxBuffer fallback;
0493:
0494:                /** A {@link Map} of the parameters to supply to the included source. */
0495:                protected Map parameters;
0496:
0497:                /** The current parameter name captured. */
0498:                protected String parameter;
0499:
0500:                /** The current parameter value (as a {@link StringBuffer}). */
0501:                protected StringBuffer value;
0502:
0503:                /** Create include element */
0504:                private IncludeElement(String base, boolean parallel,
0505:                        boolean recursive, boolean recursiveParallel,
0506:                        Logger logger) {
0507:                    this .base = base;
0508:                    this .parallel = parallel;
0509:                    this .recursive = recursive;
0510:                    this .recursiveParallel = recursiveParallel;
0511:                    this .enableLogging(logger);
0512:                }
0513:
0514:                /**
0515:                 * Process element into the buffer.
0516:                 * This can not be shared buffer, as it must be cleaned if fallback is invoked.
0517:                 */
0518:                public void process(SaxBuffer buffer) throws SAXException {
0519:                    try {
0520:                        process0(buffer, buffer);
0521:                    } catch (SAXException e) {
0522:                        buffer.recycle();
0523:                        if (this .fallback == null) {
0524:                            throw e;
0525:                        }
0526:
0527:                        if (getLogger().isInfoEnabled()) {
0528:                            getLogger().info(
0529:                                    "Failed to load <" + this .source
0530:                                            + ">, using fallback.", e);
0531:                        }
0532:                        // Stream fallback through IncludeXMLPipe
0533:                        this .fallback.toSAX(new IncludeXMLPipe(getLogger(),
0534:                                buffer, buffer, recursive,
0535:                                recursiveParallel ? parallel : false,
0536:                                recursiveParallel));
0537:                    }
0538:                }
0539:
0540:                /** Load URI into the provided handlers, process fallback */
0541:                public void process(ContentHandler contentHandler,
0542:                        LexicalHandler lexicalHandler) throws SAXException {
0543:                    try {
0544:                        if (this .fallback != null) {
0545:                            SaxBuffer buffer = new SaxBuffer();
0546:                            process(buffer);
0547:                            buffer.toSAX(contentHandler);
0548:                        } else {
0549:                            process0(contentHandler, lexicalHandler);
0550:                        }
0551:                    } catch (SAXException e) {
0552:                        // source must not be cached if an error occurs
0553:                        validity = null;
0554:                        throw e;
0555:                    }
0556:                }
0557:
0558:                /** Load URI into the provided handlers. */
0559:                private void process0(ContentHandler contentHandler,
0560:                        LexicalHandler lexicalHandler) throws SAXException {
0561:                    Source source = null;
0562:                    if (getLogger().isDebugEnabled()) {
0563:                        getLogger().debug("Loading <" + this .source + ">");
0564:                    }
0565:
0566:                    // Setup this thread's environment
0567:                    try {
0568:                        if (base != null) {
0569:                            source = resolver.resolveURI(this .source, base,
0570:                                    null);
0571:                        } else {
0572:                            source = resolver.resolveURI(this .source);
0573:                        }
0574:                        if (validity != null) {
0575:                            synchronized (validity) {
0576:                                validity.addSource(source);
0577:                            }
0578:                        }
0579:
0580:                        // Include source
0581:                        if (this .parse && recursive) {
0582:                            SourceUtil
0583:                                    .toSAX(
0584:                                            manager,
0585:                                            source,
0586:                                            this .mimeType,
0587:                                            new IncludeXMLPipe(
0588:                                                    getLogger(),
0589:                                                    contentHandler,
0590:                                                    lexicalHandler,
0591:                                                    recursive,
0592:                                                    recursiveParallel ? parallel
0593:                                                            : false,
0594:                                                    recursiveParallel));
0595:                        } else if (this .parse) {
0596:                            SourceUtil.toSAX(manager, source, this .mimeType,
0597:                                    new IncludeXMLConsumer(contentHandler,
0598:                                            lexicalHandler));
0599:                        } else {
0600:                            SourceUtil.toCharacters(source, "utf-8",
0601:                                    contentHandler);
0602:                        }
0603:
0604:                        if (getLogger().isDebugEnabled()) {
0605:                            getLogger().debug("Loaded <" + this .source + ">");
0606:                        }
0607:                    } catch (SAXException e) {
0608:                        if (getLogger().isDebugEnabled()) {
0609:                            getLogger().debug(
0610:                                    "Failed to load <" + this .source + ">", e);
0611:                        }
0612:
0613:                        throw e;
0614:
0615:                    } catch (ProcessingException e) {
0616:                        if (getLogger().isDebugEnabled()) {
0617:                            getLogger().debug(
0618:                                    "Failed to load <" + this .source + ">", e);
0619:                        }
0620:
0621:                        throw new SAXException(e);
0622:
0623:                    } catch (IOException e) {
0624:                        if (getLogger().isDebugEnabled()) {
0625:                            getLogger().debug(
0626:                                    "Failed to load <" + this .source + ">", e);
0627:                        }
0628:
0629:                        throw new SAXException(e);
0630:
0631:                    } finally {
0632:                        if (source != null) {
0633:                            resolver.release(source);
0634:                        }
0635:                    }
0636:                }
0637:            }
0638:
0639:            /**
0640:             * XML pipe reacting on the elements in the include namespace.
0641:             */
0642:            private class IncludeXMLPipe extends AbstractXMLPipe {
0643:
0644:                //
0645:                // Configuration
0646:                //
0647:
0648:                /** Indicates whether this is root include pipe (owned by transformer) or a nested one */
0649:                private final boolean root;
0650:
0651:                /** Parameter controlling recursive includes processing */
0652:                private boolean recursive;
0653:
0654:                /** Parameter controlling parallel (in multiple threads) includes processing */
0655:                private boolean parallel;
0656:
0657:                /** Parameter controlling parallel (in multiple threads) includes processing in recursive includes */
0658:                private boolean recursiveParallel;
0659:
0660:                //
0661:                // Current state
0662:                //
0663:
0664:                /** Stack of {@link XMLConsumer}s */
0665:                private final Stack consumers = new Stack();
0666:
0667:                /** Current depth of nested elements in the include namespace */
0668:                private int depth;
0669:
0670:                /** Base URI used for the resolving included sources */
0671:                private String base;
0672:
0673:                /** The source to be included declared in an include element. */
0674:                private IncludeElement element;
0675:
0676:                /** If parallel processing is enabled, then this boolean tells us whether buffering has started yet. */
0677:                private boolean buffering;
0678:
0679:                /**
0680:                 * <p>The IncludeBuffer that is used to buffering events if parallel
0681:                 * processing is turned on.</p>
0682:                 * <p>This object is also used as a lock for the thread counter <code>threads</code>.</p>
0683:                 */
0684:                private SaxBuffer buffer;
0685:
0686:                /** Inclusion threads/tasks counter (if executing in parallel) */
0687:                private int threads;
0688:
0689:                /**
0690:                 * <p>Create a new {@link IncludeXMLPipe} instance.</p>
0691:                 */
0692:                public IncludeXMLPipe() {
0693:                    root = true;
0694:                }
0695:
0696:                /**
0697:                 * <p>Create a new {@link IncludeXMLPipe} instance.</p>
0698:                 */
0699:                public IncludeXMLPipe(Logger logger,
0700:                        ContentHandler contentHandler,
0701:                        LexicalHandler lexicalHandler, boolean recursive,
0702:                        boolean parallel, boolean recursiveParallel) {
0703:                    root = false;
0704:                    this .enableLogging(logger);
0705:                    this .setContentHandler(contentHandler);
0706:                    this .setLexicalHandler(lexicalHandler);
0707:                    this .recursive = recursive;
0708:                    this .parallel = parallel;
0709:                    this .recursiveParallel = recursiveParallel;
0710:                }
0711:
0712:                /**
0713:                 * Finish processing.
0714:                 */
0715:                public void recycle() {
0716:                    if (this .buffering) {
0717:                        // Wait for threads to complete and release Sources
0718:                        waitForThreads();
0719:                        this .buffering = false;
0720:                        this .buffer = null;
0721:                    }
0722:                    this .threads = 0;
0723:
0724:                    this .consumers.clear();
0725:                    this .base = null;
0726:                    this .element = null;
0727:
0728:                    super .recycle();
0729:                }
0730:
0731:                /** Push current consumer into the stack, replace with new one */
0732:                private void push(XMLConsumer consumer) {
0733:                    this .consumers.push(new Object[] { super .xmlConsumer,
0734:                            super .contentHandler, super .lexicalHandler });
0735:                    this .setConsumer(consumer);
0736:                }
0737:
0738:                /** Pop consumer from the stack, replace current one */
0739:                private void pop() {
0740:                    Object[] consumer = (Object[]) this .consumers.pop();
0741:                    if (consumer[0] != null) {
0742:                        this .setConsumer((XMLConsumer) consumer[0]);
0743:                    } else {
0744:                        this .setContentHandler((ContentHandler) consumer[1]);
0745:                        this .setLexicalHandler((LexicalHandler) consumer[2]);
0746:                    }
0747:                }
0748:
0749:                //
0750:                // ContentHandler interface
0751:                //
0752:
0753:                public void setDocumentLocator(Locator locator) {
0754:                    try {
0755:                        if (locator != null && locator.getSystemId() != null) {
0756:                            Source source = resolver.resolveURI(locator
0757:                                    .getSystemId());
0758:                            try {
0759:                                base = source.getURI();
0760:                            } finally {
0761:                                resolver.release(source);
0762:                            }
0763:                        }
0764:                    } catch (IOException e) {
0765:                        getLogger().warn(
0766:                                "Unable to resolve document base URI: <"
0767:                                        + locator.getSystemId() + ">");
0768:                    }
0769:
0770:                    super .setDocumentLocator(locator);
0771:                }
0772:
0773:                /**
0774:                 * <p>Receive notification of the beginning of an XML document.</p>
0775:                 * @see ContentHandler#startDocument
0776:                 */
0777:                public void startDocument() throws SAXException {
0778:                    if (root) {
0779:                        super .startDocument();
0780:                    }
0781:                }
0782:
0783:                /**
0784:                 * <p>Receive notification of the end of an XML document.</p>
0785:                 * @see ContentHandler#startDocument
0786:                 */
0787:                public void endDocument() throws SAXException {
0788:                    /* This is the end of the line - process the buffered events */
0789:                    if (this .buffering) {
0790:                        pop();
0791:                        this .buffer.toSAX(super .contentHandler);
0792:                    }
0793:
0794:                    if (root) {
0795:                        super .endDocument();
0796:                    }
0797:                }
0798:
0799:                /**
0800:                 * <p>Receive notification of the start of an element.</p>
0801:                 * @see ContentHandler#startElement
0802:                 */
0803:                public void startElement(String uri, String localName,
0804:                        String qName, Attributes atts) throws SAXException {
0805:
0806:                    /* Check the namespace declaration */
0807:                    if (NS_URI.equals(uri)) {
0808:
0809:                        /*
0810:                         * Depth 0: Outside of any include tag
0811:                         * Depth 1: Must be Inside <include> tag
0812:                         * Depth 2: Inside <fallback> tag
0813:                         */
0814:                        depth++;
0815:
0816:                        /* Inclusion will not happen here but when we close this tag */
0817:                        if (INCLUDE_ELEMENT.equals(localName) && depth == 1) {
0818:                            /* Check before we include (we don't want nested stuff) */
0819:                            if (element != null) {
0820:                                throw new SAXException("Element "
0821:                                        + INCLUDE_ELEMENT
0822:                                        + " nested in another one.");
0823:                            }
0824:                            element = new IncludeElement(this .base,
0825:                                    this .parallel, this .recursive,
0826:                                    this .recursiveParallel, getLogger());
0827:
0828:                            /* Remember the source we are trying to include */
0829:                            element.source = atts.getValue(SRC_ATTRIBUTE);
0830:                            if (element.source == null
0831:                                    || element.source.length() == 0) {
0832:                                throw new SAXException("Attribute '"
0833:                                        + SRC_ATTRIBUTE + "' empty or missing.");
0834:                            }
0835:
0836:                            /* Defaults to 'xml' */
0837:                            String value = atts.getValue(PARSE_ATTRIBUTE);
0838:                            if (value == null || value.equals("xml")) {
0839:                                element.parse = true;
0840:                            } else if (value.equals("text")) {
0841:                                element.parse = false;
0842:                            } else {
0843:                                throw new SAXException("Attribute '"
0844:                                        + PARSE_ATTRIBUTE
0845:                                        + "' has invalid value.");
0846:                            }
0847:
0848:                            /* Defaults to 'text/xml' */
0849:                            element.mimeType = atts.getValue(MIME_ATTRIBUTE);
0850:                            if (!element.parse && element.mimeType != null) {
0851:                                throw new SAXException(
0852:                                        "Attribute '"
0853:                                                + MIME_ATTRIBUTE
0854:                                                + "' can't be specified for text inclusions.");
0855:                            } else if (element.mimeType == null) {
0856:                                element.mimeType = "text/xml";
0857:                            }
0858:
0859:                            /* Ignore nested content */
0860:                            push(new NOPRecorder() {
0861:                            });
0862:
0863:                            /* Done with this element */
0864:                            return;
0865:                        }
0866:
0867:                        /* If this is a fallback parameter, capture its content. */
0868:                        if (FALLBACK_ELEMENT.equals(localName) && depth == 2) {
0869:                            /* Check if we are in the right context */
0870:                            if (element == null) {
0871:                                throw new SAXException("Element "
0872:                                        + FALLBACK_ELEMENT
0873:                                        + " specified outside of "
0874:                                        + INCLUDE_ELEMENT + ".");
0875:                            }
0876:                            if (element.fallback != null) {
0877:                                throw new SAXException("Duplicate element "
0878:                                        + FALLBACK_ELEMENT + ".");
0879:                            }
0880:
0881:                            /* Buffer fallback content */
0882:                            push(element.fallback = new SaxBuffer());
0883:
0884:                            /* Done with this element */
0885:                            return;
0886:                        }
0887:
0888:                        /* If this is a parameter, then make sure we prepare. */
0889:                        if (PARAMETER_ELEMENT.equals(localName) && depth == 2) {
0890:                            /* Check if we are in the right context */
0891:                            if (element == null) {
0892:                                throw new SAXException("Element "
0893:                                        + PARAMETER_ELEMENT
0894:                                        + " specified outside of "
0895:                                        + INCLUDE_ELEMENT + ".");
0896:                            }
0897:                            if (element.parameter != null) {
0898:                                throw new SAXException("Element "
0899:                                        + PARAMETER_ELEMENT
0900:                                        + " nested in another one.");
0901:                            }
0902:
0903:                            /* Get and process the parameter name */
0904:                            element.parameter = atts.getValue(NAME_ATTRIBUTE);
0905:                            if (element.parameter == null
0906:                                    || element.parameter.length() == 0) {
0907:                                throw new SAXException("Attribute '"
0908:                                        + NAME_ATTRIBUTE
0909:                                        + "' empty or missing.");
0910:                            }
0911:
0912:                            /* Make some room for the parameter value */
0913:                            String value = atts.getValue(VALUE_ATTRIBUTE);
0914:                            if (value != null) {
0915:                                element.value = new StringBuffer(value);
0916:                            }
0917:
0918:                            /* Done with this element */
0919:                            return;
0920:                        }
0921:
0922:                        /* We don't have a clue of why we got here (wrong element?) */
0923:                        if (depth < 2) {
0924:                            throw new SAXException("Element '" + localName
0925:                                    + "' was not expected here.");
0926:                        }
0927:                    }
0928:
0929:                    super .startElement(uri, localName, qName, atts);
0930:                }
0931:
0932:                /**
0933:                 * <p>Receive notification of the end of an element.</p>
0934:                 * @see ContentHandler#endElement
0935:                 */
0936:                public void endElement(String uri, String localName,
0937:                        String qName) throws SAXException {
0938:                    /* Check the namespace declaration */
0939:                    if (NS_URI.equals(uri)) {
0940:
0941:                        /*
0942:                         * Depth 0: Outside of any include tag
0943:                         * Depth 1: Inside <include> tag
0944:                         * Depth 2: Inside <fallback> tag
0945:                         */
0946:                        depth--;
0947:
0948:                        /* Inclusion will happen here, when we close the include element */
0949:                        if (INCLUDE_ELEMENT.equals(localName) && depth == 0) {
0950:                            /* End ignoring nested content */
0951:                            pop();
0952:
0953:                            /* Get the source discovered opening the element and include */
0954:                            if (element.parameters != null) {
0955:                                element.source = NetUtils.parameterize(
0956:                                        element.source, element.parameters);
0957:                                element.parameters = null;
0958:                            }
0959:
0960:                            /* Check for parallel processing */
0961:                            if (this .parallel) {
0962:                                if (!this .buffering) {
0963:                                    this .buffering = true;
0964:                                    buffer = new SaxBuffer();
0965:                                    push(buffer);
0966:                                }
0967:
0968:                                /* Process include element in separate thread */
0969:                                buffer.xmlizable(new IncludeBuffer(element));
0970:
0971:                            } else {
0972:                                /* Process include element inline */
0973:                                element.process(super .contentHandler,
0974:                                        super .lexicalHandler);
0975:                            }
0976:
0977:                            /* We are done with this include element */
0978:                            this .element = null;
0979:                            return;
0980:                        }
0981:
0982:                        if (FALLBACK_ELEMENT.equals(localName) && depth == 1) {
0983:                            /* End buffering fallback content */
0984:                            pop();
0985:
0986:                            /* Done with this element */
0987:                            return;
0988:                        }
0989:
0990:                        /* Addition of parameters happens here (so that we can capture chars) */
0991:                        if (PARAMETER_ELEMENT.equals(localName) && depth == 1) {
0992:                            String value = (element.value != null ? element.value
0993:                                    .toString()
0994:                                    : "");
0995:
0996:                            /* Store the parameter name and value */
0997:                            try {
0998:                                /*
0999:                                 * Note: the parameter name and value are URL encoded, so that
1000:                                 * weird characters such as "&" or "=" (have special meaning)
1001:                                 * are passed through flawlessly.
1002:                                 */
1003:                                if (element.parameters == null) {
1004:                                    element.parameters = new HashMap(5);
1005:                                }
1006:                                element.parameters.put(NetUtils.encode(
1007:                                        element.parameter, ENCODING), NetUtils
1008:                                        .encode(value, ENCODING));
1009:                            } catch (UnsupportedEncodingException e) {
1010:                                throw new SAXException(
1011:                                        "Your platform does not support the "
1012:                                                + ENCODING + " encoding", e);
1013:                            }
1014:
1015:                            /* We are done with this parameter element */
1016:                            element.value = null;
1017:                            element.parameter = null;
1018:                            return;
1019:                        }
1020:                    }
1021:
1022:                    /* This is not our namespace, pass the event on! */
1023:                    super .endElement(uri, localName, qName);
1024:                }
1025:
1026:                /**
1027:                 * <p>Receive notification of characters.</p>
1028:                 * @see ContentHandler#characters
1029:                 */
1030:                public void characters(char[] data, int offset, int length)
1031:                        throws SAXException {
1032:                    if (element != null && element.parameter != null) {
1033:                        /* If we have a parameter value to add to, let's add this chunk */
1034:                        if (element.value == null) {
1035:                            element.value = new StringBuffer();
1036:                        }
1037:                        element.value.append(data, offset, length);
1038:                        return;
1039:                    }
1040:
1041:                    /* Forward */
1042:                    super .characters(data, offset, length);
1043:                }
1044:
1045:                //
1046:                // Thread management
1047:                //
1048:
1049:                /**
1050:                 * Increment active threads counter
1051:                 */
1052:                int incrementThreads() {
1053:                    synchronized (buffer) {
1054:                        return ++threads;
1055:                    }
1056:                }
1057:
1058:                /**
1059:                 * Decrement active threads counter
1060:                 */
1061:                void decrementThreads() {
1062:                    synchronized (buffer) {
1063:                        if (--threads <= 0) {
1064:                            buffer.notify();
1065:                        }
1066:                    }
1067:                }
1068:
1069:                /**
1070:                 * Wait till there is no active threads
1071:                 */
1072:                private void waitForThreads() {
1073:                    synchronized (buffer) {
1074:                        if (threads > 0) {
1075:                            if (getLogger().isDebugEnabled()) {
1076:                                getLogger()
1077:                                        .debug(
1078:                                                threads
1079:                                                        + " threads in progress, waiting");
1080:                            }
1081:
1082:                            try {
1083:                                buffer.wait();
1084:                            } catch (InterruptedException e) { /* ignored */
1085:                            }
1086:                            // Don't continue waiting if interrupted.
1087:                        }
1088:                    }
1089:                }
1090:
1091:                /**
1092:                 * Buffer for loading included source in separate thread.
1093:                 * Streaming of the loaded buffer possible only when source is
1094:                 * loaded completely. If loading is not complete, toSAX method
1095:                 * will block.
1096:                 */
1097:                private class IncludeBuffer extends SaxBuffer implements 
1098:                        Runnable {
1099:
1100:                    private IncludeElement element;
1101:                    private int thread;
1102:                    private boolean finished;
1103:                    private SAXException e;
1104:
1105:                    public IncludeBuffer(IncludeElement element) {
1106:                        this .element = element;
1107:
1108:                        RunnableManager runnable = null;
1109:                        try {
1110:                            runnable = (RunnableManager) IncludeTransformer.this .manager
1111:                                    .lookup(RunnableManager.ROLE);
1112:                            runnable.execute(
1113:                                    IncludeTransformer.this .threadPool, this );
1114:                        } catch (final ServiceException e) {
1115:                            // In case we failed to spawn a thread
1116:                            throw new CascadingRuntimeException(e.getMessage(),
1117:                                    e);
1118:                        } finally {
1119:                            IncludeTransformer.this .manager.release(runnable);
1120:                        }
1121:
1122:                        // Increment active threads counter
1123:                        this .thread = incrementThreads();
1124:                    }
1125:
1126:                    /**
1127:                     * Load content of the source into this buffer.
1128:                     */
1129:                    public void run() {
1130:                        try {
1131:                            if (getLogger().isDebugEnabled()) {
1132:                                getLogger().debug(
1133:                                        "Thread #" + thread + " loading <"
1134:                                                + element.source + ">");
1135:                            }
1136:
1137:                            // Setup this thread's environment
1138:                            CocoonComponentManager.enterEnvironment(
1139:                                    environment, new WrapperComponentManager(
1140:                                            manager), processor);
1141:                            try {
1142:                                element.process(this );
1143:
1144:                            } catch (SAXException e) {
1145:                                this .e = e;
1146:
1147:                            } finally {
1148:                                CocoonComponentManager.leaveEnvironment();
1149:                            }
1150:                        } catch (RuntimeException e) {
1151:                            /* Unable to set thread's environment */
1152:                            this .e = new SAXException(e);
1153:
1154:                        } finally {
1155:                            synchronized (this ) {
1156:                                this .finished = true;
1157:                                notify();
1158:                            }
1159:
1160:                            // Make sure that active threads counter is decremented
1161:                            decrementThreads();
1162:                        }
1163:
1164:                        if (getLogger().isDebugEnabled()) {
1165:                            if (this .e == null) {
1166:                                getLogger().debug(
1167:                                        "Thread #" + thread + " loaded <"
1168:                                                + element.source + ">");
1169:                            } else {
1170:                                getLogger().debug(
1171:                                        "Thread #" + thread
1172:                                                + " failed to load <"
1173:                                                + element.source + ">", this .e);
1174:                            }
1175:                        }
1176:                    }
1177:
1178:                    /**
1179:                     * Stream content of this buffer when it is loaded completely.
1180:                     * This method blocks if loading is not complete.
1181:                     */
1182:                    public void toSAX(ContentHandler contentHandler)
1183:                            throws SAXException {
1184:                        synchronized (this ) {
1185:                            if (!this .finished) {
1186:                                try {
1187:                                    wait();
1188:                                } catch (InterruptedException e) { /* ignored */
1189:                                }
1190:                                // Don't continue waiting if interrupted.
1191:                            }
1192:                        }
1193:
1194:                        if (this.e != null) {
1195:                            throw this.e;
1196:                        }
1197:
1198:                        super.toSAX(contentHandler);
1199:                    }
1200:                }
1201:            }
1202:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.