cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sylv...@apache.org
Subject svn commit: r326716 - in /cocoon/branches/BRANCH_2_1_X: ./ src/java/org/apache/cocoon/ src/java/org/apache/cocoon/components/xslt/ src/java/org/apache/cocoon/transformation/ src/java/org/apache/cocoon/util/log/ src/webapp/WEB-INF/
Date Wed, 19 Oct 2005 21:44:54 GMT
Author: sylvain
Date: Wed Oct 19 14:44:41 2005
New Revision: 326716

URL: http://svn.apache.org/viewcvs?rev=326716&view=rev
Log:
Refactor XSLT error handling. We now have *all* messages!

Added:
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
  (with props)
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
  (with props)
Modified:
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/TraxTransformer.java
    cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java
    cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
    cocoon/branches/BRANCH_2_1_X/status.xml

Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles?rev=326716&r1=326715&r2=326716&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles Wed Oct 19 14:44:41
2005
@@ -71,7 +71,7 @@
   <!-- XSLT: -->
   <role name="org.apache.excalibur.xml.xslt.XSLTProcessor"
         shorthand="xslt-processor"
-        default-class="org.apache.excalibur.xml.xslt.XSLTProcessorImpl"/>
+        default-class="org.apache.cocoon.components.xslt.TraxProcessor"/>
 
   <role name="org.apache.excalibur.xml.xpath.XPathProcessor"
         shorthand="xpath-processor"

Added: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java?rev=326716&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
(added)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
Wed Oct 19 14:44:41 2005
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.util.location.LocationUtils;
+
+/**
+ * A smart error listener for <code>javax.xml.tranform</code> that does its best
to provide
+ * useful error messages.
+ * 
+ * @version $Id$
+ * @since 2.1.8
+ */
+public class TraxErrorListener implements ErrorListener{
+
+    private Logger logger;
+    private String uri;
+    
+    /** The exception we had from warning() */
+    private TransformerException warningEx;
+    
+    /** The exception we had from error() or fatalError() */
+    private TransformerException exception;
+
+    public TraxErrorListener(Logger logger, String uri) {
+        this.logger = logger;
+        this.uri = uri;
+    }
+
+    /**
+     * Get the exception that was catched by this listener, if any.
+     * 
+     * @return the exception
+     */
+    public Throwable getThrowable() {
+        if (exception == null) {
+            return null;
+        }
+        
+        Location loc = LocationUtils.getLocation(exception);
+        if (LocationUtils.isKnown(loc)) {
+            // Has a location: don't loose this precious information!
+            return exception;
+        }
+        
+        // No location: if it's just a wrapper, consider only the wrapped exception
+        if (exception.getCause() != null) {
+            return exception.getCause();
+        }
+        
+        // That's the actual exception!
+        return exception;
+    }
+
+    public void warning(TransformerException ex) throws TransformerException {
+        // TODO: We may want here to allow some special formatting of the messages, such
as
+        // "DEBUG:A debug message" or "INFO:Transforming <foo> in mode 'bar'" to use
the different
+        // log levels. This can include also deprecation logs for system-defined stylesheets
+        // using "DEPRECATED:WARN:Styling 'foo' is replaced by 'bar'".    
+
+        if (logger.isWarnEnabled()) {
+            Location loc = LocationUtils.getLocation(ex);
+            logger.warn(ex.getMessage() + " at "+ loc == null ? uri : loc.toString());
+        }
+        // Keep the warning (see below)
+        warningEx = ex;
+    }
+
+    public void error(TransformerException ex) throws TransformerException {
+
+        // If we had a warning previoulsy, and the current exception has no cause, then use
the warning.
+        // This is how Xalan behaves on <xsl:message terminate="yes">: it first issues
a warning with all
+        // the useful information, then a useless "stylesheed directed termination" error.
+        if (warningEx != null && ex.getCause() == null) {
+            ex = warningEx;
+        }
+        warningEx = null;
+
+        // Keep the exception for later use.
+        exception = ex;
+        // and rethrow it
+        throw ex;
+    }
+
+    public void fatalError(TransformerException ex) throws TransformerException {
+        if (warningEx != null && ex.getCause() == null) {
+            ex = warningEx;
+        }
+        warningEx = null;
+
+        exception = ex;
+        throw ex;
+    }
+}

Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxErrorListener.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java?rev=326716&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
(added)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
Wed Oct 19 14:44:41 2005
@@ -0,0 +1,639 @@
+/* 
+ * Copyright 2005 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.xslt;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TemplatesHandler;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.parameters.ParameterException;
+import org.apache.avalon.framework.parameters.Parameterizable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.impl.validity.AggregatedValidity;
+import org.apache.excalibur.store.Store;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.apache.excalibur.xml.xslt.XSLTProcessor;
+import org.apache.excalibur.xml.xslt.XSLTProcessorException;
+import org.apache.excalibur.xmlizer.XMLizer;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+
+/**
+ * Adaptation of Excalibur's XSLTProcessor implementation to allow for better
+ * error reporting.
+ * 
+ * @version $Id$
+ * @since 2.1.8
+ */
+
+public class TraxProcessor extends AbstractLogEnabled implements XSLTProcessor, Serviceable,
Initializable, Disposable, Parameterizable,
+        Recyclable, URIResolver {
+    /** The store service instance */
+    protected Store m_store;
+
+    /** The configured transformer factory to use */
+    protected String m_transformerFactory;
+
+    /** The trax TransformerFactory this component uses */
+    protected SAXTransformerFactory m_factory;
+
+    /** The default TransformerFactory used by this component */
+    protected SAXTransformerFactory m_defaultFactory;
+
+    /** Is the store turned on? (default is off) */
+    protected boolean m_useStore;
+
+    /** Is incremental processing turned on? (default for Xalan: no) */
+    protected boolean m_incrementalProcessing;
+
+    /** Resolver used to resolve XSLT document() calls, imports and includes */
+    protected SourceResolver m_resolver;
+
+    /** Check included stylesheets */
+    protected boolean m_checkIncludes;
+
+    /** Map of pairs of System ID's / validities of the included stylesheets */
+    protected Map m_includesMap = new HashMap();
+
+    protected XMLizer m_xmlizer;
+
+    /** The ServiceManager */
+    protected ServiceManager m_manager;
+
+    /**
+     * Compose. Try to get the store
+     * 
+     * @avalon.service interface="XMLizer"
+     * @avalon.service interface="SourceResolver"
+     * @avalon.service interface="Store/TransientStore" optional="true"
+     */
+    public void service(final ServiceManager manager) throws ServiceException {
+        m_manager = manager;
+        m_xmlizer = (XMLizer) m_manager.lookup(XMLizer.ROLE);
+        m_resolver = (SourceResolver) m_manager.lookup(SourceResolver.ROLE);
+
+        if (m_manager.hasService(Store.TRANSIENT_STORE)) {
+            m_store = (Store) m_manager.lookup(Store.TRANSIENT_STORE);
+        }
+    }
+
+    /**
+     * Initialize
+     */
+    public void initialize() throws Exception {
+        m_factory = getTransformerFactory(m_transformerFactory);
+        m_defaultFactory = m_factory;
+    }
+
+    /**
+     * Disposable
+     */
+    public void dispose() {
+        if (null != m_manager) {
+            m_manager.release(m_store);
+            m_manager.release(m_resolver);
+            m_manager.release(m_xmlizer);
+            m_manager = null;
+        }
+        m_xmlizer = null;
+        m_store = null;
+        m_resolver = null;
+    }
+
+    /**
+     * Configure the component
+     */
+    public void parameterize(final Parameters params) throws ParameterException {
+        m_useStore = params.getParameterAsBoolean("use-store", this.m_useStore);
+        m_incrementalProcessing = params.getParameterAsBoolean("incremental-processing",
this.m_incrementalProcessing);
+        m_transformerFactory = params.getParameter("transformer-factory", null);
+        m_checkIncludes = params.getParameterAsBoolean("check-includes", true);
+        if (!m_useStore) {
+            // release the store, if we don't need it anymore
+            m_manager.release(m_store);
+            m_store = null;
+        } else if (null == m_store) {
+            final String message = "XSLTProcessor: use-store is set to true, " + "but unable
to aquire the Store.";
+            throw new ParameterException(message);
+        }
+    }
+
+    /**
+     * Set the transformer factory used by this component
+     */
+    public void setTransformerFactory(final String classname) {
+        m_factory = getTransformerFactory(classname);
+    }
+
+    /**
+     * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandler(org.apache.excalibur.source.Source)
+     */
+    public TransformerHandler getTransformerHandler(final Source stylesheet) throws XSLTProcessorException
{
+        return getTransformerHandler(stylesheet, null);
+    }
+
+    /**
+     * @see org.apache.excalibur.xml.xslt.XSLTProcessor#getTransformerHandler(org.apache.excalibur.source.Source,
+     *      org.xml.sax.XMLFilter)
+     */
+    public TransformerHandler getTransformerHandler(final Source stylesheet, final XMLFilter
filter) throws XSLTProcessorException {
+        final XSLTProcessor.TransformerHandlerAndValidity validity = getTransformerHandlerAndValidity(stylesheet,
filter);
+        return validity.getTransfomerHandler();
+    }
+
+    public TransformerHandlerAndValidity getTransformerHandlerAndValidity(final Source stylesheet)
throws XSLTProcessorException {
+        return getTransformerHandlerAndValidity(stylesheet, null);
+    }
+
+    public TransformerHandlerAndValidity getTransformerHandlerAndValidity(Source stylesheet,
XMLFilter filter) throws XSLTProcessorException {
+
+        final String id = stylesheet.getURI();
+        TransformerHandlerAndValidity handlerAndValidity;
+
+        try {
+            handlerAndValidity = getTemplates(stylesheet, id);
+            if (handlerAndValidity != null) {
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Reusing Templates for " + id);
+                }
+                return handlerAndValidity;
+            }
+        } catch(Exception e) {
+            throw new XSLTProcessorException("Error retrieving template", e);
+        }
+        
+        TraxErrorListener errorListener = new TraxErrorListener(getLogger(), stylesheet.getURI());
+        try{
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Creating new Templates for " + id);
+            }
+
+            m_factory.setErrorListener(errorListener);
+
+            // Create a Templates ContentHandler to handle parsing of the
+            // stylesheet.
+            TemplatesHandler templatesHandler = m_factory.newTemplatesHandler();
+
+            // Set the system ID for the template handler since some
+            // TrAX implementations (XSLTC) rely on this in order to obtain
+            // a meaningful identifier for the Templates instances.
+            templatesHandler.setSystemId(id);
+            if (filter != null) {
+                filter.setContentHandler(templatesHandler);
+            }
+
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Source = " + stylesheet + ", templatesHandler = " + templatesHandler);
+            }
+
+            // Initialize List for included validities
+            SourceValidity validity = stylesheet.getValidity();
+            if (validity != null && m_checkIncludes) {
+                m_includesMap.put(id, new ArrayList());
+            }
+
+            try {
+                // Process the stylesheet.
+                sourceToSAX(stylesheet, filter != null ? (ContentHandler) filter : (ContentHandler)
templatesHandler);
+
+                // Get the Templates object (generated during the parsing of
+                // the stylesheet) from the TemplatesHandler.
+                final Templates template = templatesHandler.getTemplates();
+
+                if (null == template) {
+                    throw new XSLTProcessorException("Unable to create templates for stylesheet:
" + stylesheet.getURI());
+                }
+
+                putTemplates(template, stylesheet, id);
+
+                // Create transformer handler
+                final TransformerHandler handler = m_factory.newTransformerHandler(template);
+                handler.getTransformer().setErrorListener(new TraxErrorListener(getLogger(),
stylesheet.getURI()));
+                handler.getTransformer().setURIResolver(this);
+
+                // Create aggregated validity
+                AggregatedValidity aggregated = null;
+                if (validity != null && m_checkIncludes) {
+                    List includes = (List) m_includesMap.get(id);
+                    if (includes != null) {
+                        aggregated = new AggregatedValidity();
+                        aggregated.add(validity);
+                        for (int i = includes.size() - 1; i >= 0; i--) {
+                            aggregated.add((SourceValidity) ((Object[]) includes.get(i))[1]);
+                        }
+                        validity = aggregated;
+                    }
+                }
+
+                // Create result
+                handlerAndValidity = new MyTransformerHandlerAndValidity(handler, validity);
+            } finally {
+                if (m_checkIncludes)
+                    m_includesMap.remove(id);
+            }
+
+            return handlerAndValidity;
+        } catch (Exception e) {
+            Throwable realEx = errorListener.getThrowable();
+            if (realEx == null) realEx = e;
+            
+            if (realEx instanceof RuntimeException) {
+                throw (RuntimeException)realEx;
+            }
+            
+            if (realEx instanceof XSLTProcessorException) {
+                throw (XSLTProcessorException)realEx;
+            }
+            
+            throw new XSLTProcessorException("Exception when creating Transformer from "
+ stylesheet.getURI(), realEx);
+        }
+    }
+
+    private void sourceToSAX(Source source, ContentHandler handler) throws SAXException,
IOException, SourceException {
+        if (source instanceof XMLizable) {
+            ((XMLizable) source).toSAX(handler);
+        } else {
+            final InputStream inputStream = source.getInputStream();
+            final String mimeType = source.getMimeType();
+            final String systemId = source.getURI();
+            m_xmlizer.toSAX(inputStream, mimeType, systemId, handler);
+        }
+    }
+
+    public void transform(final Source source, final Source stylesheet, final Parameters
params, final Result result) throws XSLTProcessorException {
+        try {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug(
+                        "Transform source = " + source + ", stylesheet = " + stylesheet +
", parameters = " + params + ", result = " + result);
+            }
+            final TransformerHandler handler = getTransformerHandler(stylesheet);
+            if (params != null) {
+                final Transformer transformer = handler.getTransformer();
+                transformer.clearParameters();
+                String[] names = params.getNames();
+                for (int i = names.length - 1; i >= 0; i--) {
+                    transformer.setParameter(names[i], params.getParameter(names[i]));
+                }
+            }
+
+            handler.setResult(result);
+            sourceToSAX(source, handler);
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Transform done");
+            }
+        } catch (SAXException e) {
+            // Unwrapping the exception will "remove" the real cause with
+            // never Xalan versions and makes the exception message unusable
+            final String message = "Error in running Transformation";
+            throw new XSLTProcessorException(message, e);
+            /*
+             * if( e.getException() == null ) { final String message = "Error in
+             * running Transformation"; throw new XSLTProcessorException(
+             * message, e ); } else { final String message = "Got SAXException.
+             * Rethrowing cause exception."; getLogger().debug( message, e );
+             * throw new XSLTProcessorException( "Error in running
+             * Transformation", e.getException() ); }
+             */
+        } catch (Exception e) {
+            final String message = "Error in running Transformation";
+            throw new XSLTProcessorException(message, e);
+        }
+    }
+
+    /**
+     * Get the TransformerFactory associated with the given classname. If the
+     * class can't be found or the given class doesn't implement the required
+     * interface, the default factory is returned.
+     */
+    private SAXTransformerFactory getTransformerFactory(String factoryName) {
+        SAXTransformerFactory _factory;
+
+        if (null == factoryName) {
+            _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+        } else {
+            try {
+                ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                if (loader == null) {
+                    loader = getClass().getClassLoader();
+                }
+                _factory = (SAXTransformerFactory) loader.loadClass(factoryName).newInstance();
+            } catch (ClassNotFoundException cnfe) {
+                getLogger().error("Cannot find the requested TrAX factory '" + factoryName
+ "'. Using default TrAX Transformer Factory instead.");
+                if (m_factory != null)
+                    return m_factory;
+                _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+            } catch (ClassCastException cce) {
+                getLogger().error(
+                        "The indicated class '" + factoryName
+                                + "' is not a TrAX Transformer Factory. Using default TrAX
Transformer Factory instead.");
+                if (m_factory != null)
+                    return m_factory;
+                _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+            } catch (Exception e) {
+                getLogger().error(
+                        "Error found loading the requested TrAX Transformer Factory '" +
factoryName
+                                + "'. Using default TrAX Transformer Factory instead.");
+                if (m_factory != null)
+                    return m_factory;
+                _factory = (SAXTransformerFactory) TransformerFactory.newInstance();
+            }
+        }
+
+        _factory.setErrorListener(new TraxErrorListener(getLogger(), null));
+        _factory.setURIResolver(this);
+
+        // FIXME (SM): implementation-specific parameter passing should be
+        // made more extensible.
+        if (_factory.getClass().getName().equals("org.apache.xalan.processor.TransformerFactoryImpl"))
{
+            _factory.setAttribute("http://xml.apache.org/xalan/features/incremental", new
Boolean(m_incrementalProcessing));
+        }
+
+        return _factory;
+    }
+
+    private TransformerHandlerAndValidity getTemplates(Source stylesheet, String id) throws
IOException, TransformerException {
+        if (!m_useStore) {
+            return null;
+        }
+
+        // we must augment the template ID with the factory classname since one
+        // transformer implementation cannot handle the instances of a
+        // template created by another one.
+        String key = "XSLTTemplate: " + id + '(' + m_factory.getClass().getName() + ')';
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("getTemplates: stylesheet " + id);
+        }
+
+        SourceValidity newValidity = stylesheet.getValidity();
+
+        // Only stylesheets with validity are stored
+        if (newValidity == null) {
+            // Remove an old template
+            m_store.remove(key);
+            return null;
+        }
+
+        // Stored is an array of the templates and the caching time and list of
+        // includes
+        Object[] templateAndValidityAndIncludes = (Object[]) m_store.get(key);
+        if (templateAndValidityAndIncludes == null) {
+            // Templates not found in cache
+            return null;
+        }
+
+        // Check template modification time
+        SourceValidity storedValidity = (SourceValidity) templateAndValidityAndIncludes[1];
+        int valid = storedValidity.isValid();
+        boolean isValid;
+        if (valid == 0) {
+            valid = storedValidity.isValid(newValidity);
+            isValid = (valid == 1);
+        } else {
+            isValid = (valid == 1);
+        }
+        if (!isValid) {
+            m_store.remove(key);
+            return null;
+        }
+
+        // Check includes
+        if (m_checkIncludes) {
+            AggregatedValidity aggregated = null;
+            List includes = (List) templateAndValidityAndIncludes[2];
+            if (includes != null) {
+                aggregated = new AggregatedValidity();
+                aggregated.add(storedValidity);
+
+                for (int i = includes.size() - 1; i >= 0; i--) {
+                    // Every include stored as pair of source ID and validity
+                    Object[] pair = (Object[]) includes.get(i);
+                    storedValidity = (SourceValidity) pair[1];
+                    aggregated.add(storedValidity);
+
+                    valid = storedValidity.isValid();
+                    isValid = false;
+                    if (valid == 0) {
+                        Source includedSource = null;
+                        try {
+                            includedSource = m_resolver.resolveURI((String) pair[0]);
+                            SourceValidity included = includedSource.getValidity();
+                            if (included != null) {
+                                valid = storedValidity.isValid(included);
+                                isValid = (valid == 1);
+                            }
+                        } finally {
+                            m_resolver.release(includedSource);
+                        }
+                    } else {
+                        isValid = (valid == 1);
+                    }
+                    if (!isValid) {
+                        m_store.remove(key);
+                        return null;
+                    }
+                }
+                storedValidity = aggregated;
+            }
+        }
+
+        TransformerHandler handler = m_factory.newTransformerHandler((Templates) templateAndValidityAndIncludes[0]);
+        handler.getTransformer().setErrorListener(new TraxErrorListener(getLogger(), stylesheet.getURI()));
+        handler.getTransformer().setURIResolver(this);
+        return new MyTransformerHandlerAndValidity(handler, storedValidity);
+    }
+
+    private void putTemplates(Templates templates, Source stylesheet, String id) throws IOException
{
+        if (!m_useStore)
+            return;
+
+        // we must augment the template ID with the factory classname since one
+        // transformer implementation cannot handle the instances of a
+        // template created by another one.
+        String key = "XSLTTemplate: " + id + '(' + m_factory.getClass().getName() + ')';
+
+        // only stylesheets with a last modification date are stored
+        SourceValidity validity = stylesheet.getValidity();
+        if (null != validity) {
+            // Stored is an array of the template and the current time
+            Object[] templateAndValidityAndIncludes = new Object[3];
+            templateAndValidityAndIncludes[0] = templates;
+            templateAndValidityAndIncludes[1] = validity;
+            if (m_checkIncludes) {
+                templateAndValidityAndIncludes[2] = m_includesMap.get(id);
+            }
+            m_store.store(key, templateAndValidityAndIncludes);
+        }
+    }
+
+    /**
+     * Called by the processor when it encounters an xsl:include, xsl:import, or
+     * document() function.
+     * 
+     * @param href
+     *            An href attribute, which may be relative or absolute.
+     * @param base
+     *            The base URI in effect when the href attribute was
+     *            encountered.
+     * 
+     * @return A Source object, or null if the href cannot be resolved, and the
+     *         processor should try to resolve the URI itself.
+     * 
+     * @throws TransformerException
+     *             if an error occurs when trying to resolve the URI.
+     */
+    public javax.xml.transform.Source resolve(String href, String base) throws TransformerException
{
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("resolve(href = " + href + ", base = " + base + "); resolver
= " + m_resolver);
+        }
+
+        Source xslSource = null;
+        try {
+            if (base == null || href.indexOf(":") > 1) {
+                // Null base - href must be an absolute URL
+                xslSource = m_resolver.resolveURI(href);
+            } else if (href.length() == 0) {
+                // Empty href resolves to base
+                xslSource = m_resolver.resolveURI(base);
+            } else {
+                // is the base a file or a real m_url
+                if (!base.startsWith("file:")) {
+                    int lastPathElementPos = base.lastIndexOf('/');
+                    if (lastPathElementPos == -1) {
+                        // this should never occur as the base should
+                        // always be protocol:/....
+                        return null; // we can't resolve this
+                    } else {
+                        xslSource = m_resolver.resolveURI(base.substring(0, lastPathElementPos)
+ "/" + href);
+                    }
+                } else {
+                    File parent = new File(base.substring(5));
+                    File parent2 = new File(parent.getParentFile(), href);
+                    xslSource = m_resolver.resolveURI(parent2.toURL().toExternalForm());
+                }
+            }
+
+            InputSource is = getInputSource(xslSource);
+
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("xslSource = " + xslSource + ", system id = " + xslSource.getURI());
+            }
+
+            if (m_checkIncludes) {
+                // Populate included validities
+                List includes = (List) m_includesMap.get(base);
+                if (includes != null) {
+                    SourceValidity included = xslSource.getValidity();
+                    if (included != null) {
+                        includes.add(new Object[] { xslSource.getURI(), xslSource.getValidity()
});
+                    } else {
+                        // One of the included stylesheets is not cacheable
+                        m_includesMap.remove(base);
+                    }
+                }
+            }
+
+            return new StreamSource(is.getByteStream(), is.getSystemId());
+        } catch (SourceException e) {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return
null", e);
+            }
+
+            // CZ: To obtain the same behaviour as when the resource is
+            // transformed by the XSLT Transformer we should return null here.
+            return null;
+        } catch (java.net.MalformedURLException mue) {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return
null", mue);
+            }
+
+            return null;
+        } catch (IOException ioe) {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return
null", ioe);
+            }
+
+            return null;
+        } finally {
+            m_resolver.release(xslSource);
+        }
+    }
+
+    /**
+     * Return a new <code>InputSource</code> object that uses the
+     * <code>InputStream</code> and the system ID of the <code>Source</code>
+     * object.
+     * 
+     * @throws IOException
+     *             if I/O error occured.
+     */
+    private static InputSource getInputSource(final Source source) throws IOException, SourceException
{
+        final InputSource newObject = new InputSource(source.getInputStream());
+        newObject.setSystemId(source.getURI());
+        return newObject;
+    }
+
+    /**
+     * Recycle the component
+     */
+    public void recycle() {
+        m_includesMap.clear();
+        // restore default factory
+        if (m_factory != m_defaultFactory) {
+            m_factory = m_defaultFactory;
+        }
+    }
+
+    /**
+     * Subclass to allow for instanciation, as for some unknown reason the
+     * constructor is protected....
+     */
+    public static class MyTransformerHandlerAndValidity extends TransformerHandlerAndValidity
{
+
+        protected MyTransformerHandlerAndValidity(TransformerHandler handler, SourceValidity
validity) {
+            super(handler, validity);
+        }
+    }
+}

Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/xslt/TraxProcessor.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/TraxTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/TraxTransformer.java?rev=326716&r1=326715&r2=326716&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/TraxTransformer.java
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/TraxTransformer.java
Wed Oct 19 14:44:41 2005
@@ -26,8 +26,6 @@
 import java.util.Set;
 import java.util.Map.Entry;
 
-import javax.xml.transform.ErrorListener;
-import javax.xml.transform.TransformerException;
 import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.sax.TransformerHandler;
 
@@ -43,13 +41,12 @@
 import org.apache.cocoon.ProcessingException;
 import org.apache.cocoon.caching.CacheableProcessingComponent;
 import org.apache.cocoon.components.source.SourceUtil;
+import org.apache.cocoon.components.xslt.TraxErrorListener;
 import org.apache.cocoon.environment.Cookie;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.Session;
 import org.apache.cocoon.environment.SourceResolver;
-import org.apache.cocoon.util.location.Location;
-import org.apache.cocoon.util.location.LocationUtils;
 import org.apache.cocoon.xml.XMLConsumer;
 import org.apache.commons.lang.BooleanUtils;
 import org.apache.commons.lang.exception.NestableRuntimeException;
@@ -204,31 +201,8 @@
     /** Exception that might occur during setConsumer */
     private SAXException exceptionDuringSetConsumer;
 
-    private TransformerException transformerException;
-
-    private ErrorListener errorListener = new ErrorListener() {
-
-        public void warning(TransformerException ex) throws TransformerException {
-            if (getLogger().isWarnEnabled()) {
-                Location loc = LocationUtils.getLocation(ex);
-                getLogger().warn("Warning at " + loc == null ? inputSource.getURI() : loc.toString(),
ex);
-            }
-        }
-
-        public void error(TransformerException ex) throws TransformerException {
-            if (getLogger().isWarnEnabled()) {
-                Location loc = LocationUtils.getLocation(ex);
-                getLogger().error("Error at " + loc == null ? inputSource.getURI() : loc.toString(),
ex);
-            }
-        }
-
-        public void fatalError(TransformerException ex) throws TransformerException {
-            // Keep it for later use
-            transformerException = ex;
-            // and rethrow it
-            throw ex;
-        }
-    };
+    /** The error listener used by the stylesheet */
+    private TraxErrorListener errorListener;
 
     /**
      * Configure this transformer.
@@ -442,6 +416,7 @@
         result.setLexicalHandler(consumer);
         this.transformerHandler.setResult(result);
 
+        this.errorListener = new TraxErrorListener(getLogger(), this.inputSource.getURI());
         this.transformerHandler.getTransformer().setErrorListener(this.errorListener);
     }
 
@@ -597,7 +572,7 @@
         this.transformerHandler = null;
         this.transformerValidity = null;
         this.exceptionDuringSetConsumer = null;
-        this.transformerException = null;
+        this.errorListener = null;
         super.recycle();
     }
 
@@ -609,21 +584,9 @@
         try {
             super.endDocument();
         } catch(Exception e) {
-            Throwable realEx = e;
             
-            // Did we had an exception reported to the error listener?
-            if (transformerException != null) {
-                // this is the real exception
-                Location loc = LocationUtils.getLocation(transformerException);
-                if (LocationUtils.isUnknown(loc)) {
-                    // No location: if it's just a wrapper, consider only the wrapped exception
-                    realEx = transformerException.getCause();
-                    if (realEx == null) {
-                        // no cause: keep the transformer exception
-                        realEx = transformerException;
-                    }
-                }
-            }
+            Throwable realEx = this.errorListener.getThrowable();
+            if (realEx == null) realEx = e;
             
             if (realEx instanceof RuntimeException) {
                 throw (RuntimeException)realEx;
@@ -633,10 +596,6 @@
                 throw (SAXException)realEx;
             }
             
-            if (realEx instanceof Exception) {
-                throw new SAXException((Exception)realEx);
-            }
-        
             if (realEx instanceof Error) {
                 throw (Error)realEx;
             }

Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java?rev=326716&r1=326715&r2=326716&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java
Wed Oct 19 14:44:41 2005
@@ -15,13 +15,12 @@
  */
 package org.apache.cocoon.util.log;
 
-import org.apache.avalon.framework.ExceptionUtil;
-import org.apache.avalon.framework.logger.LogKitLogger;
+import java.util.Map;
 
+import org.apache.avalon.framework.logger.LogKitLogger;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.util.location.LocatedException;
-
 import org.apache.commons.lang.ClassUtils;
 import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.commons.lang.time.FastDateFormat;
@@ -29,8 +28,6 @@
 import org.apache.log.LogEvent;
 import org.apache.log.Logger;
 
-import java.util.Map;
-
 /**
  * An extended pattern formatter. New patterns defined by this class are:
  * <ul>
@@ -171,7 +168,9 @@
             case TYPE_QUERY:
                 return getQueryString(event.getContextMap());
             case TYPE_ROOTTHROWABLE:
-                return getStackTrace(ExceptionUtils.getRootCause(event.getThrowable()), run.m_format);
+                Throwable thr = event.getThrowable();
+                Throwable root = ExceptionUtils.getRootCause(thr); // Can be null if no cause
+                return getStackTrace(root == null ? thr : root, run.m_format);
         }
         return super.formatPatternRun(event, run);
     }

Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf?rev=326716&r1=326715&r2=326716&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf Wed Oct 19 14:44:41 2005
@@ -383,7 +383,7 @@
       +-->
   <component logger="core.xslt"
              role="org.apache.excalibur.xml.xslt.XSLTProcessor/xsltc"
-             class="org.apache.excalibur.xml.xslt.XSLTProcessorImpl">
+             class="org.apache.cocoon.components.xslt.TraxProcessor">
      <parameter name="use-store" value="true"/>
      <parameter name="transformer-factory" value="org.apache.xalan.xsltc.trax.TransformerFactoryImpl"/>
   </component>
@@ -393,7 +393,7 @@
       +-->
   <component logger="core.xslt"
              role="org.apache.excalibur.xml.xslt.XSLTProcessor/xalan"
-             class="org.apache.excalibur.xml.xslt.XSLTProcessorImpl">
+             class="org.apache.cocoon.components.xslt.TraxProcessor">
      <parameter name="use-store" value="true"/>
      <parameter name="incremental-processing" value="false"/>
      <parameter name="transformer-factory" value="org.apache.xalan.processor.TransformerFactoryImpl"/>
@@ -407,7 +407,7 @@
       |  <parameter name="transformer-factory" value="net.sf.saxon.TransformerFactoryImpl"/>
   <component logger="core.xslt"
              role="org.apache.excalibur.xml.xslt.XSLTProcessor/saxon"
-             class="org.apache.excalibur.xml.xslt.XSLTProcessorImpl">
+             class="org.apache.cocoon.components.xslt.TraxProcessor">
      <parameter name="use-store" value="true"/>
      <parameter name="transformer-factory" value="com.icl.saxon.TransformerFactoryImpl"/>
   </component>

Modified: cocoon/branches/BRANCH_2_1_X/status.xml
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/status.xml?rev=326716&r1=326715&r2=326716&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Wed Oct 19 14:44:41 2005
@@ -404,9 +404,12 @@
       is allows to access location and nested exceptions for Cocoon stacktraces.
     </action>
     <action dev="SW" type="fix">
-      Add a specific ErrorListener in TraxTransformer to track exceptions raised during XSLT
transformations.
-      This allows this transformer to throw the exception that actually occured, rather than
a useless RuntimeException
-      as reported by Xalan.
+      Complete refactoring of error handling in TraxTransformer and XSLTProcessor so that
actual error messages
+      are displayed rather than the useless RuntimeException usually reported by Xalan. These
messages include
+      both stylesheet compilation errors and warnings, and outputs of <code>&lt;xsl:message
terminate="yes"&gt;</code>.
+      <br/>
+      Using this feature requires to change the XSTLProcessor class in <code>cocoon.xconf</code>
to
+      <code>org.apache.cocoon.components.xslt.TraxProcessor</code>.
     </action>
     <action dev="AN" type="add">
       XSP block: Extend {#expr} interpolation parser to understand {}, "}", '}'



Mime
View raw message