Author: vgritsenko Date: Fri Mar 11 12:59:21 2005 New Revision: 157155 URL: http://svn.apache.org/viewcvs?view=rev&rev=157155 Log: Refactor sitemap error handling: * Encapsulate error handling details into ErrorHandlerHelper * Always log exception into handled-erorrs log, both in map:pipeline and map:pipelines * Error handling in map:pipelines gets same logic as in map:pipeline
Modified: cocoon/trunk/src/java/org/apache/cocoon/components/source/impl/SitemapSource.java cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ErrorHandlerHelper.java cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelineNode.java cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNode.java cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNodeBuilder.java Modified: cocoon/trunk/src/java/org/apache/cocoon/components/source/impl/SitemapSource.java URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/components/source/impl/SitemapSource.java?view=diff&r1=157154&r2=157155 ============================================================================== --- cocoon/trunk/src/java/org/apache/cocoon/components/source/impl/SitemapSource.java (original) +++ cocoon/trunk/src/java/org/apache/cocoon/components/source/impl/SitemapSource.java Fri Mar 11 12:59:21 2005 @@ -299,7 +299,7 @@ if (redirectURL.indexOf(":") == -1) { redirectURL = this.protocol + ":/" + redirectURL; } - if ( this.sourceResolver == null ) { + if (this.sourceResolver == null) { this.sourceResolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE); } this.redirectSource = this.sourceResolver.resolveURI(redirectURL); @@ -311,8 +311,7 @@ this.exception = e; } catch (Exception e) { reset(); - this.exception = new SAXException("Could not get sitemap source " - + this.systemId, e); + this.exception = new SAXException("Could not get sitemap source " + this.systemId, e); } this.needsRefresh = false; } @@ -347,7 +346,7 @@ this.environment); try { this.pipelineDescription.processingPipeline.process(this.environment, - EnvironmentHelper.createEnvironmentAwareConsumer(consumer)); + EnvironmentHelper.createEnvironmentAwareConsumer(consumer)); } finally { EnvironmentHelper.leaveProcessor(); } @@ -356,8 +355,7 @@ // Preserve original exception throw e; } catch (Exception e) { - throw new SAXException("Exception during processing of " - + this.systemId, e); + throw new SAXException("Exception during processing of " + this.systemId, e); } finally { this.needsRefresh = true; } Modified: cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ErrorHandlerHelper.java URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ErrorHandlerHelper.java?view=diff&r1=157154&r2=157155 ============================================================================== --- cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ErrorHandlerHelper.java (original) +++ cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/ErrorHandlerHelper.java Fri Mar 11 12:59:21 2005 @@ -1,12 +1,12 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. - * + * Copyright 1999-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. @@ -15,12 +15,14 @@ */ package org.apache.cocoon.components.treeprocessor.sitemap; -import java.util.Map; - import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; + import org.apache.cocoon.Constants; +import org.apache.cocoon.ResourceNotFoundException; import org.apache.cocoon.components.notification.Notifying; import org.apache.cocoon.components.notification.NotifyingBuilder; import org.apache.cocoon.components.treeprocessor.InvokeContext; @@ -28,6 +30,9 @@ import org.apache.cocoon.environment.Environment; import org.apache.cocoon.environment.ObjectModelHelper; +import java.io.IOException; +import java.util.Map; + /** * Helps to call error handlers from PipelineNode and PipelinesNode. * @@ -35,69 +40,128 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Björn Lütkemeier</a> * @version CVS $Id$ */ -public class ErrorHandlerHelper extends AbstractLogEnabled implements Serviceable { +public class ErrorHandlerHelper extends AbstractLogEnabled + implements Serviceable { private ServiceManager manager; /** + * Logger for handled errors + */ + protected Logger handledErrorsLogger; + + /** + * Error handling node for the ResourceNotFoundException + */ + private ProcessingNode error404; + + /** + * Error handling node for all other exceptions + */ + private ProcessingNode error500; + + + public void enableLogging(Logger logger) { + super.enableLogging(logger); + this.handledErrorsLogger = logger.getChildLogger("handled-errors"); + } + + /** * The component manager is used to create notifying builders. */ public void service(ServiceManager manager) { this.manager = manager; } - public boolean invokeErrorHandler(ProcessingNode node, Exception ex, Environment env, InvokeContext originalContext) + void setHandledErrorsLogger(Logger logger) { + this.handledErrorsLogger = logger; + } + + void set404Handler(ProcessingNode node) { + this.error404 = node; + } + + void set500Handler(ProcessingNode node) { + this.error500 = node; + } + + public boolean invokeErrorHandler(Exception ex, + Environment env, + InvokeContext context) throws Exception { - Map objectModel = env.getObjectModel(); - - InvokeContext errorContext = null; - boolean nodeSuccessful = false; - + if (!env.isExternal() && !env.isInternalRedirect()) { + // Propagate exception on internal requests + throw ex; + } else if (error404 != null && ex instanceof ResourceNotFoundException) { + // Invoke 404-specific handler + return invokeErrorHandler(error404, ex, env, context); + } else if (error500 != null) { + // Invoke global handler + return invokeErrorHandler(error500, ex, env, context); + } + + // No handler : propagate + throw ex; + } + + public boolean invokeErrorHandler(ProcessingNode node, + Exception ex, + Environment env, + InvokeContext context) + throws Exception { + this.handledErrorsLogger.error(ex.getMessage(), ex); + try { - if (objectModel.get(Constants.NOTIFYING_OBJECT) == null) { - // error has not been processed by another handler before - - // Try to reset the response to avoid mixing already produced output - // and error page. - env.tryResetResponse(); - - // Create a Notifying - NotifyingBuilder notifyingBuilder= (NotifyingBuilder)this.manager.lookup(NotifyingBuilder.ROLE); - Notifying currentNotifying = null; - try { - currentNotifying = notifyingBuilder.build(this, ex); - } finally { - this.manager.release(notifyingBuilder); - } - - // Add it to the object model - objectModel.put(Constants.NOTIFYING_OBJECT, currentNotifying); - - // Also add the exception - objectModel.put(ObjectModelHelper.THROWABLE_OBJECT, ex); - } - - // Build a new context - errorContext = new InvokeContext(); - errorContext.enableLogging(getLogger()); - errorContext.service(this.manager); - errorContext.setRedirector(originalContext.getRedirector()); - - nodeSuccessful = node.invoke(env, errorContext); - } catch (Exception subEx) { - getLogger().error("An exception occured while handling errors at " + node.getLocation(), subEx); - // Rethrow it : it will either be handled by the parent sitemap or by the environment (e.g. Cocoon servlet) - throw subEx; - } finally { - if (errorContext != null) { + prepare(env, ex); + + // Create error context + InvokeContext errorContext = new InvokeContext(context.isBuildingPipelineOnly()); + errorContext.enableLogging(getLogger()); + errorContext.setRedirector(context.getRedirector()); + errorContext.service(this.manager); + try { + // Process error handling node + if (node.invoke(env, errorContext)) { + // Exception was handled. + return true; + } + } finally { errorContext.dispose(); } + } catch (Exception e) { + getLogger().error("An exception occured while handling errors at " + node.getLocation(), e); + // Rethrow it: It will either be handled by the parent sitemap or by the environment (e.g. Cocoon servlet) + throw e; } - - if (nodeSuccessful) { - return true; - } + + // Exception was not handled in this error handler, propagate. throw ex; } -} + private void prepare(Environment env, Exception ex) + throws IOException, ServiceException { + Map objectModel = env.getObjectModel(); + if (objectModel.get(Constants.NOTIFYING_OBJECT) == null) { + // error has not been processed by another handler before + + // Try to reset the response to avoid mixing already produced output + // and error page. + env.tryResetResponse(); + + // Create a Notifying + NotifyingBuilder notifyingBuilder = (NotifyingBuilder) this.manager.lookup(NotifyingBuilder.ROLE); + Notifying currentNotifying = null; + try { + currentNotifying = notifyingBuilder.build(this, ex); + } finally { + this.manager.release(notifyingBuilder); + } + + // Add it to the object model + objectModel.put(Constants.NOTIFYING_OBJECT, currentNotifying); + + // Also add the exception + objectModel.put(ObjectModelHelper.THROWABLE_OBJECT, ex); + } + } +} Modified: cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelineNode.java URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelineNode.java?view=diff&r1=157154&r2=157155 ============================================================================== --- cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelineNode.java (original) +++ cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelineNode.java Fri Mar 11 12:59:21 2005 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-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. @@ -15,20 +15,20 @@ */ package org.apache.cocoon.components.treeprocessor.sitemap; -import java.util.Map; - import org.apache.avalon.framework.logger.Logger; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; + import org.apache.cocoon.ConnectionResetException; import org.apache.cocoon.ResourceNotFoundException; import org.apache.cocoon.components.treeprocessor.AbstractParentProcessingNode; import org.apache.cocoon.components.treeprocessor.InvokeContext; import org.apache.cocoon.components.treeprocessor.ParameterizableProcessingNode; import org.apache.cocoon.components.treeprocessor.ProcessingNode; -import org.apache.cocoon.components.treeprocessor.sitemap.MountNode; import org.apache.cocoon.environment.Environment; +import java.util.Map; + /** * Handles <map:pipeline> * @@ -37,34 +37,27 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> * @author <a href="mailto:[EMAIL PROTECTED]">Gianugo Rabellino</a> * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a> - * @version CVS $Id$ + * @version $Id$ */ -public class PipelineNode - extends AbstractParentProcessingNode - implements Serviceable, ParameterizableProcessingNode { +public class PipelineNode extends AbstractParentProcessingNode + implements Serviceable, ParameterizableProcessingNode { - // TODO : handle a 'fail-hard' environment attribute - // can be useful to stop off-line generation when there's an error + // TODO: handle a 'fail-hard' environment attribute + // can be useful to stop off-line generation when there's an error private ProcessingNode[] children; - private ProcessingNode error404; - - private ProcessingNode error500; - - private ErrorHandlerHelper errorHandlerHelper = new ErrorHandlerHelper(); + private ErrorHandlerHelper errorHandlerHelper; - protected Logger handledErrorsLogger; - - private boolean internalOnly = false; + private boolean internalOnly; /** Is it the last <pipeline> in the enclosing <pipelines> ? */ - private boolean isLast = false; + private boolean isLast; /** The component name of the processing pipeline */ protected String processingPipeline; - /** Optional Sitemap parameters */ + /** Optional sitemap parameters */ protected Map parameters; /** @@ -73,6 +66,7 @@ */ public PipelineNode(String name) { this.processingPipeline = name; + this.errorHandlerHelper = new ErrorHandlerHelper(); } /** @@ -85,7 +79,6 @@ public void enableLogging(Logger logger) { super.enableLogging(logger); this.errorHandlerHelper.enableLogging(logger); - handledErrorsLogger = logger.getChildLogger("handled-errors"); } public void setChildren(ProcessingNode[] nodes) { @@ -101,11 +94,11 @@ } public void set404Handler(ProcessingNode node) { - this.error404 = node; + this.errorHandlerHelper.set404Handler(node); } public void set500Handler(ProcessingNode node) { - this.error500 = node; + this.errorHandlerHelper.set500Handler(node); } public void setInternalOnly(boolean internalOnly) { @@ -114,54 +107,37 @@ public final boolean invoke(Environment env, InvokeContext context) throws Exception { - boolean passThrough; - Object passThroughRaw = env.getAttribute(MountNode.COCOON_PASS_THROUGH); - - if(passThroughRaw == null){ - //use legacy default value + if (passThroughRaw == null) { + // Use default value passThrough = false; - }else{ - passThrough = ((Boolean)passThroughRaw).booleanValue() ; + } else { + passThrough = ((Boolean) passThroughRaw).booleanValue(); } - - boolean externalRequest = env.isExternal(); // Always fail on external request if pipeline is internal only. - if (this.internalOnly && externalRequest) { + if (this.internalOnly && env.isExternal()) { return false; } - context.inform(this.processingPipeline, this.parameters, - env.getObjectModel()); + + context.inform(this.processingPipeline, this.parameters, env.getObjectModel()); try { if (invokeNodes(children, env, context)) { return true; } else if (!this.isLast || passThrough) { return false; - } else { - throw new ResourceNotFoundException("No pipeline matched request: " + - env.getURIPrefix() + env.getURI()); } - } catch (ConnectionResetException cre) { + + throw new ResourceNotFoundException("No pipeline matched request: " + + env.getURIPrefix() + env.getURI()); + + } catch (ConnectionResetException e) { // Will be reported by CocoonServlet, rethrowing - throw cre; - } catch (Exception ex) { - if (!externalRequest && !env.isInternalRedirect()) { - // Propagate exception on internal requests - throw ex; - } else if (error404 != null && ex instanceof ResourceNotFoundException) { - // Invoke 404-specific handler - handledErrorsLogger.error(ex.getMessage(), ex); - return errorHandlerHelper.invokeErrorHandler(error404, ex, env, context); - } else if (error500 != null) { - // Invoke global handler - handledErrorsLogger.error(ex.getMessage(), ex); - return errorHandlerHelper.invokeErrorHandler(error500, ex, env, context); - } else { - // No handler : propagate - throw ex; - } + throw e; + } catch (Exception e) { + // Invoke error handler + return this.errorHandlerHelper.invokeErrorHandler(e, env, context); } } } Modified: cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNode.java URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNode.java?view=diff&r1=157154&r2=157155 ============================================================================== --- cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNode.java (original) +++ cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNode.java Fri Mar 11 12:59:21 2005 @@ -19,6 +19,8 @@ import org.apache.avalon.framework.logger.Logger; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; + +import org.apache.cocoon.ConnectionResetException; import org.apache.cocoon.components.treeprocessor.InvokeContext; import org.apache.cocoon.components.treeprocessor.ProcessingNode; import org.apache.cocoon.components.treeprocessor.SimpleParentProcessingNode; @@ -31,22 +33,21 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Björn Lütkemeier</a> * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a> - * @version CVS $Id$ + * @version $Id$ */ public final class PipelinesNode extends SimpleParentProcessingNode implements Serviceable, Disposable { private ServiceManager manager; - private ErrorHandlerHelper errorHandlerHelper = new ErrorHandlerHelper(); - - private ProcessingNode errorHandler; + private ErrorHandlerHelper errorHandlerHelper; /** * Constructor */ public PipelinesNode() { super(null); + this.errorHandlerHelper = new ErrorHandlerHelper(); } /** @@ -64,13 +65,12 @@ } public void setErrorHandler(ProcessingNode node) { - this.errorHandler = node; + this.errorHandlerHelper.set500Handler(node); } public void setChildren(ProcessingNode[] nodes) { // Mark the last pipeline so that it can throw a ResourceNotFoundException - ((PipelineNode)nodes[nodes.length - 1]).setLast(true); - + ((PipelineNode) nodes[nodes.length - 1]).setLast(true); super.setChildren(nodes); } @@ -89,17 +89,17 @@ context.service(this.manager); try { - // FIXME : is there any useful information that can be passed as top-level parameters, - // such as the URI of the mount point ? + // FIXME: Is there any useful information that can be passed as top-level parameters, + // such as the URI of the mount point ? return invokeNodes(this.children, env, context); + + } catch (ConnectionResetException e) { + // Will be reported by CocoonServlet, rethrowing + throw e; } catch (Exception ex) { - if (this.errorHandler != null) { - // Invoke pipelines handler - return this.errorHandlerHelper.invokeErrorHandler(this.errorHandler, ex, env, context); - } - // No handler : propagate - throw ex; + // Invoke pipelines handler + return this.errorHandlerHelper.invokeErrorHandler(ex, env, context); } } @@ -108,7 +108,7 @@ */ public void dispose() { if (this.manager instanceof Disposable) { - ((Disposable)this.manager).dispose(); + ((Disposable) this.manager).dispose(); } this.manager = null; } Modified: cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNodeBuilder.java URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNodeBuilder.java?view=diff&r1=157154&r2=157155 ============================================================================== --- cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNodeBuilder.java (original) +++ cocoon/trunk/src/java/org/apache/cocoon/components/treeprocessor/sitemap/PipelinesNodeBuilder.java Fri Mar 11 12:59:21 2005 @@ -1,12 +1,12 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. - * + * Copyright 1999-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. @@ -15,16 +15,17 @@ */ package org.apache.cocoon.components.treeprocessor.sitemap; -import java.util.ArrayList; -import java.util.List; - import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.thread.ThreadSafe; + import org.apache.cocoon.components.treeprocessor.ContainerNodeBuilder; import org.apache.cocoon.components.treeprocessor.ProcessingNode; import org.apache.cocoon.components.treeprocessor.ProcessingNodeBuilder; +import java.util.ArrayList; +import java.util.List; + /** * Builds a <map:pipelines> * @@ -32,47 +33,47 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Björn Lütkemeier</a> * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a> * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a> - * @version CVS $Id: PipelinesNodeBuilder.java,v 1.5 2004/03/05 13:02:52 bdelacretaz Exp $ + * @version $Id$ */ - public class PipelinesNodeBuilder extends ContainerNodeBuilder implements ThreadSafe { public ProcessingNode buildNode(Configuration config) throws Exception { - // check for component configurations + // Check for component configurations Configuration child = config.getChild("component-configurations", false); if (child != null) { - this.checkNamespace(child); + checkNamespace(child); this.treeBuilder.getProcessor().setComponentConfigurations(child); } + PipelinesNode node = new PipelinesNode(); this.treeBuilder.setupNode(node, config); - Configuration[] childConfigs = config.getChildren(); - List children = new ArrayList(); - HandleErrorsNode handler = null; - - for (int i = 0; i < childConfigs.length; i++) { - - Configuration childConfig = childConfigs[i]; - if (isChild(childConfig)) { - - ProcessingNodeBuilder builder = this.treeBuilder.createNodeBuilder(childConfig); - if (builder instanceof HandleErrorsNodeBuilder) { - handler = (HandleErrorsNode)builder.buildNode(childConfig); - } else { - // Regular builder - children.add(builder.buildNode(childConfig)); - } - } - } + Configuration[] childConfigs = config.getChildren(); + List children = new ArrayList(); + HandleErrorsNode handler = null; + + for (int i = 0; i < childConfigs.length; i++) { + Configuration childConfig = childConfigs[i]; + if (isChild(childConfig)) { + + ProcessingNodeBuilder builder = this.treeBuilder.createNodeBuilder(childConfig); + if (builder instanceof HandleErrorsNodeBuilder) { + handler = (HandleErrorsNode)builder.buildNode(childConfig); + } else { + // Regular builder + children.add(builder.buildNode(childConfig)); + } + } + } + if (children.size() == 0) { String msg = "There must be at least one pipeline at " + config.getLocation(); throw new ConfigurationException(msg); } - node.setChildren(toNodeArray(children)); - node.setErrorHandler(handler); + node.setChildren(toNodeArray(children)); + node.setErrorHandler(handler); return node; }