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.selection;
018:
019: import java.util.Map;
020:
021: import org.apache.avalon.framework.configuration.Configurable;
022: import org.apache.avalon.framework.configuration.Configuration;
023: import org.apache.avalon.framework.configuration.ConfigurationException;
024: import org.apache.avalon.framework.parameters.Parameters;
025: import org.apache.cocoon.environment.ObjectModelHelper;
026: import org.apache.cocoon.util.ClassUtils;
027: import org.apache.commons.lang.exception.ExceptionUtils;
028:
029: /**
030: * In a <map:handle-errors>, selects depending on the exception that caused the error.
031: * The configuration of this selector allows to map exception class names to symbolic names
032: * that are used in the <map:when> alternatives.
033: * <p>
034: * Example configuration :
035: * <pre>
036: * <map:selector type="error" src="....ExceptionSelector">
037: * <exception class="org.xml.sax.SAXException" name="sax" unroll="true"/>
038: * <exception name="not-found" class="org.apache.cocoon.ResourceNotFoundException"/>
039: * <exception class="org.apache.cocoon.ProcessingException" unroll="true"/>
040: * <exception name="denied" class="java.security.SecurityException"/>
041: * <exception name="denied" class="my.comp.auth.AuthenticationFailure"/>
042: * </map:selector>
043: * </pre>
044: * This example shows several features :
045: * <li>the "class" is the class name of the exception (which can be any <code>Throwable</code>),</li>
046: * <li>an exception can be given a name, which is used in the <map:when> tests,</li>
047: * <li>an exception can be unrolled, meaning we try to get its cause and then consider this cause for
048: * the exception name</li>
049: * Note that both "name" and "unroll" can be specified. In that case, we first try to unroll the exception,
050: * and if none of the causes has a name, then the "name" attribute is considered.
051: *
052: * @author <a href="mailto:juergen.seitz@basf-it-services.com">Jürgen Seitz</a>
053: * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a>
054: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
055: * @since 2.1
056: * @version CVS $Id: ExceptionSelector.java 433543 2006-08-22 06:22:54Z crossley $
057: */
058:
059: public class ExceptionSelector extends AbstractSwitchSelector implements
060: Configurable {
061:
062: /** Exception classes */
063: private Class[] clazz;
064:
065: /** Associated symbolic names (can be null) */
066: private String[] name;
067:
068: /** Do we want to unroll them ? */
069: private boolean[] unroll;
070:
071: public void configure(Configuration conf)
072: throws ConfigurationException {
073:
074: Configuration[] children = conf.getChildren("exception");
075:
076: this .clazz = new Class[children.length];
077: this .name = new String[children.length];
078: this .unroll = new boolean[children.length];
079:
080: for (int i = 0; i < children.length; i++) {
081: Configuration child = children[i];
082:
083: String childClassName = child.getAttribute("class");
084: Class childClass = null;
085: try {
086: childClass = ClassUtils.loadClass(childClassName);
087: } catch (Exception e) {
088: throw new ConfigurationException("Cannot load class '"
089: + childClassName + "' at "
090: + child.getLocation());
091: }
092:
093: // Check that this class is not hidden by a more general class already declared
094: for (int j = 0; j < i; j++) {
095: if (this .clazz[j].isAssignableFrom(childClass)) {
096: throw new ConfigurationException("Class '"
097: + this .clazz[j].getName()
098: + "' hides its subclass '" + childClassName
099: + "' at " + child.getLocation());
100: }
101: }
102:
103: this .clazz[i] = childClass;
104: this .name[i] = child.getAttribute("name", null);
105: this .unroll[i] = child.getAttributeAsBoolean("unroll",
106: false);
107:
108: if (this .name[i] == null && !this .unroll[i]) {
109: throw new ConfigurationException(
110: "Must specify one of 'name' or 'unroll' at "
111: + child.getLocation());
112: }
113: }
114: }
115:
116: /**
117: * Compute the exception type, given the configuration and the exception stored in the object model.
118: *
119: * @see ObjectModelHelper#getThrowable(java.util.Map)
120: */
121: public Object getSelectorContext(Map objectModel,
122: Parameters parameters) {
123: // Get the name of the exception
124: Throwable thr = ObjectModelHelper.getThrowable(objectModel);
125: if (thr == null) {
126: throw new IllegalStateException(
127: "No exception in object model. ExceptionSelector can only be used in <map:handle-errors>");
128: }
129:
130: return find(thr);
131: }
132:
133: private FindResult find(Throwable thr) {
134: // Now find the proper name
135: for (int i = 0; i < this .clazz.length; i++) {
136: if (this .clazz[i].isInstance(thr)) {
137:
138: // If exception needs to be unrolled, and it has a cause,
139: // return the cause name, if not null (recursively)
140: if (this .unroll[i]) {
141: Throwable cause = ExceptionUtils.getCause(thr);
142: if (cause != null) {
143: FindResult result = find(cause);
144: if (result != null) {
145: return result;
146: }
147: }
148: }
149:
150: // Not unrolled
151: return new FindResult(this .name[i], thr);
152: }
153: }
154:
155: // Not found
156: return null;
157: }
158:
159: public boolean select(String expression, Object selectorContext) {
160: if (selectorContext == null) {
161: return false;
162: }
163: // Just compare the expression with the previously found name
164: boolean result = expression
165: .equals(((FindResult) selectorContext).getName());
166:
167: if (result) {
168: if (getLogger().isDebugEnabled())
169: getLogger().debug(
170: "select succesfull for condition "
171: + selectorContext.toString());
172: }
173:
174: return result;
175: }
176:
177: static class FindResult {
178: private String name;
179: private Throwable throwable;
180:
181: public FindResult(String name, Throwable throwable) {
182: this .name = name;
183: this .throwable = throwable;
184: }
185:
186: public String getName() {
187: return this .name;
188: }
189:
190: public void setName(String name) {
191: this .name = name;
192: }
193:
194: public Throwable getThrowable() {
195: return this .throwable;
196: }
197:
198: public void setThrowable(Throwable throwable) {
199: this .throwable = throwable;
200: }
201:
202: public String toString() {
203: return this.name;
204: }
205: }
206:
207: }
|