001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.treeprocessor.sitemap;
018:
019: import org.apache.avalon.framework.component.ComponentException;
020: import org.apache.avalon.framework.component.ComponentManager;
021: import org.apache.avalon.framework.component.Composable;
022: import org.apache.avalon.framework.logger.AbstractLogEnabled;
023: import org.apache.avalon.framework.logger.Logger;
024:
025: import org.apache.cocoon.Constants;
026: import org.apache.cocoon.ResourceNotFoundException;
027: import org.apache.cocoon.components.notification.Notifying;
028: import org.apache.cocoon.components.notification.NotifyingBuilder;
029: import org.apache.cocoon.components.pipeline.ProcessingPipeline;
030: import org.apache.cocoon.components.treeprocessor.InvokeContext;
031: import org.apache.cocoon.components.treeprocessor.ProcessingNode;
032: import org.apache.cocoon.environment.Environment;
033: import org.apache.cocoon.environment.ObjectModelHelper;
034:
035: import java.io.IOException;
036: import java.util.Map;
037:
038: /**
039: * Helps to call error handlers from PipelineNode and PipelinesNode.
040: *
041: * @author <a href="mailto:juergen.seitz@basf-it-services.com">Jürgen Seitz</a>
042: * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
043: * @version $Id: ErrorHandlerHelper.java 433543 2006-08-22 06:22:54Z crossley $
044: */
045: public class ErrorHandlerHelper extends AbstractLogEnabled implements
046: Composable {
047:
048: private ComponentManager manager;
049:
050: /**
051: * Logger for handled errors
052: */
053: protected Logger handledErrorsLogger;
054:
055: /**
056: * Error handling node for the ResourceNotFoundException
057: * (deprecated)
058: */
059: private HandleErrorsNode error404;
060:
061: /**
062: * Error handling node for all other exceptions
063: */
064: private HandleErrorsNode error500;
065:
066: public void enableLogging(Logger logger) {
067: super .enableLogging(logger);
068: this .handledErrorsLogger = logger
069: .getChildLogger("handled-errors");
070: }
071:
072: /**
073: * The component manager is used to create notifying builders.
074: */
075: public void compose(ComponentManager manager) {
076: this .manager = manager;
077: }
078:
079: void set404Handler(ProcessingNode node) {
080: this .error404 = (HandleErrorsNode) node;
081: }
082:
083: void set500Handler(ProcessingNode node) {
084: this .error500 = (HandleErrorsNode) node;
085: }
086:
087: /**
088: * @return true if has no error handler nodes set
089: */
090: public boolean isEmpty() {
091: return this .error404 == null && this .error500 == null;
092: }
093:
094: public boolean isInternal() {
095: return this .error500 != null && this .error500.isInternal();
096: }
097:
098: public boolean isExternal() {
099: return this .error500 != null && this .error500.isExternal();
100: }
101:
102: /**
103: * Handle error.
104: */
105: public boolean invokeErrorHandler(Exception ex, Environment env,
106: InvokeContext context) throws Exception {
107: return prepareErrorHandler(ex, env, context) != null;
108: }
109:
110: /**
111: * Prepare error handler for the internal pipeline error handling.
112: *
113: * <p>If building pipeline only, error handling pipeline will be
114: * built and returned. If building and executing pipeline,
115: * error handling pipeline will be built and executed.</p>
116: */
117: public ProcessingPipeline prepareErrorHandler(Exception ex,
118: Environment env, InvokeContext context) throws Exception {
119: boolean internal = !env.isExternal()
120: && !env.isInternalRedirect();
121:
122: if (internal && !isInternal()) {
123: // Propagate exception on internal request: No internal handler.
124: throw ex;
125: } else if (!internal && !isExternal()) {
126: // Propagate exception on external request: No external handler.
127: throw ex;
128: } else if (!internal && error404 != null
129: && ex instanceof ResourceNotFoundException) {
130: // Invoke 404-specific handler: Only on external requests. Deprecated.
131: return prepareErrorHandler(error404, ex, env, context);
132: } else if (error500 != null) {
133: // Invoke global handler
134: return prepareErrorHandler(error500, ex, env, context);
135: }
136:
137: // Exception was not handled in this error handler, propagate.
138: throw ex;
139: }
140:
141: /**
142: * Handle error using specified error handler processing node.
143: */
144: public boolean invokeErrorHandler(ProcessingNode node,
145: Exception ex, Environment env, InvokeContext context)
146: throws Exception {
147: return prepareErrorHandler(node, ex, env, context) != null;
148: }
149:
150: /**
151: * Prepare (or execute) error handler using specified error handler
152: * processing node.
153: *
154: * <p>If building pipeline only, error handling pipeline will be
155: * built and returned. If building and executing pipeline,
156: * error handling pipeline will be built and executed.</p>
157: */
158: private ProcessingPipeline prepareErrorHandler(ProcessingNode node,
159: Exception ex, Environment env, InvokeContext context)
160: throws Exception {
161: if (ex instanceof ResourceNotFoundException) {
162: this .handledErrorsLogger.error(ex.getMessage());
163: } else {
164: this .handledErrorsLogger.error(ex.getMessage(), ex);
165: }
166:
167: try {
168: prepare(context, env, ex);
169:
170: // Create error context
171: InvokeContext errorContext = new InvokeContext(context
172: .isBuildingPipelineOnly());
173: errorContext.enableLogging(getLogger());
174: errorContext.setRedirector(context.getRedirector());
175: errorContext.compose(this .manager);
176: try {
177: // Process error handling node
178: if (node.invoke(env, errorContext)) {
179: // Exception was handled.
180: return errorContext.getProcessingPipeline();
181: }
182: } finally {
183: errorContext.dispose();
184: }
185: } catch (Exception e) {
186: getLogger().error(
187: "An exception occured while handling errors at "
188: + node.getLocation(), e);
189: // Rethrow it: It will either be handled by the parent sitemap or by the environment (e.g. Cocoon servlet)
190: throw e;
191: }
192:
193: // Exception was not handled in this error handler, propagate.
194: throw ex;
195: }
196:
197: /**
198: * Build notifying object
199: */
200: private void prepare(InvokeContext context, Environment env,
201: Exception ex) throws IOException, ComponentException {
202: Map objectModel = env.getObjectModel();
203: if (objectModel.get(Constants.NOTIFYING_OBJECT) == null) {
204: // error has not been processed by another handler before
205:
206: // Try to reset the response to avoid mixing already produced output
207: // and error page.
208: if (!context.isBuildingPipelineOnly()) {
209: env.tryResetResponse();
210: }
211:
212: // Create a Notifying
213: NotifyingBuilder notifyingBuilder = (NotifyingBuilder) this .manager
214: .lookup(NotifyingBuilder.ROLE);
215: Notifying currentNotifying = null;
216: try {
217: currentNotifying = notifyingBuilder.build(this , ex);
218: } finally {
219: this .manager.release(notifyingBuilder);
220: }
221:
222: // Add it to the object model
223: objectModel.put(Constants.NOTIFYING_OBJECT,
224: currentNotifying);
225:
226: // Also add the exception
227: objectModel.put(ObjectModelHelper.THROWABLE_OBJECT, ex);
228: }
229: }
230: }
|