1 /*
2 * $Id: ExceptionConfig.java 471754 2006-11-06 14:55:09Z husted $
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21 package org.apache.struts.config;
22
23 import java.lang.reflect.InvocationTargetException;
24
25 /**
26 * <p>A JavaBean representing the configuration information of an
27 * <code><exception></code> element from a Struts configuration
28 * file.</p>
29 *
30 * @version $Rev: 471754 $ $Date: 2005-08-06 18:03:30 -0400 (Sat, 06 Aug 2005)
31 * $
32 * @since Struts 1.1
33 */
34 public class ExceptionConfig extends BaseConfig {
35 // ------------------------------------------------------------- Properties
36
37 /**
38 * The servlet context attribute under which the message resources bundle
39 * to be used for this exception is located. If not set, the default
40 * message resources for the current module is assumed.
41 */
42 protected String bundle = null;
43
44 /**
45 * The type of the ExceptionConfig that this object should inherit
46 * properties from.
47 */
48 protected String inherit = null;
49
50 /**
51 * Have the inheritance values for this class been applied?
52 */
53 protected boolean extensionProcessed = false;
54
55 /**
56 * The fully qualified Java class name of the exception handler class
57 * which should be instantiated to handle this exception.
58 */
59 protected String handler = "org.apache.struts.action.ExceptionHandler";
60
61 /**
62 * The message resources key specifying the error message associated with
63 * this exception.
64 */
65 protected String key = null;
66
67 /**
68 * The module-relative path of the resource to forward to if this
69 * exception occurs during an <code>Action</code>.
70 */
71 protected String path = null;
72
73 /**
74 * The scope in which we should expose the ActionMessage for this
75 * exception handler.
76 */
77 protected String scope = "request";
78
79 /**
80 * The fully qualified Java class name of the exception that is to be
81 * handled by this handler.
82 */
83 protected String type = null;
84
85 public String getBundle() {
86 return (this.bundle);
87 }
88
89 public void setBundle(String bundle) {
90 if (configured) {
91 throw new IllegalStateException("Configuration is frozen");
92 }
93
94 this.bundle = bundle;
95 }
96
97 public String getExtends() {
98 return (this.inherit);
99 }
100
101 public void setExtends(String inherit) {
102 if (configured) {
103 throw new IllegalStateException("Configuration is frozen");
104 }
105
106 this.inherit = inherit;
107 }
108
109 public boolean isExtensionProcessed() {
110 return extensionProcessed;
111 }
112
113 public String getHandler() {
114 return (this.handler);
115 }
116
117 public void setHandler(String handler) {
118 if (configured) {
119 throw new IllegalStateException("Configuration is frozen");
120 }
121
122 this.handler = handler;
123 }
124
125 public String getKey() {
126 return (this.key);
127 }
128
129 public void setKey(String key) {
130 if (configured) {
131 throw new IllegalStateException("Configuration is frozen");
132 }
133
134 this.key = key;
135 }
136
137 public String getPath() {
138 return (this.path);
139 }
140
141 public void setPath(String path) {
142 if (configured) {
143 throw new IllegalStateException("Configuration is frozen");
144 }
145
146 this.path = path;
147 }
148
149 public String getScope() {
150 return (this.scope);
151 }
152
153 public void setScope(String scope) {
154 if (configured) {
155 throw new IllegalStateException("Configuration is frozen");
156 }
157
158 this.scope = scope;
159 }
160
161 public String getType() {
162 return (this.type);
163 }
164
165 public void setType(String type) {
166 if (configured) {
167 throw new IllegalStateException("Configuration is frozen");
168 }
169
170 this.type = type;
171 }
172
173 // ------------------------------------------------------ Protected Methods
174
175 /**
176 * <p>Traces the hierarchy of this object to check if any of the ancestors
177 * are extending this instance.</p>
178 *
179 * @param moduleConfig The {@link ModuleConfig} that this config is from.
180 * @param actionConfig The {@link ActionConfig} that this config is from,
181 * if applicable. This parameter must be null if this
182 * is a global handler.
183 * @return true if circular inheritance was detected.
184 */
185 protected boolean checkCircularInheritance(ModuleConfig moduleConfig,
186 ActionConfig actionConfig) {
187 String ancestorType = getExtends();
188
189 if (ancestorType == null) {
190 return false;
191 }
192
193 // Find our ancestor
194 ExceptionConfig ancestor = null;
195
196 // First check the action config
197 if (actionConfig != null) {
198 ancestor = actionConfig.findExceptionConfig(ancestorType);
199
200 // If we found *this*, set ancestor to null to check for a global def
201 if (ancestor == this) {
202 ancestor = null;
203 }
204 }
205
206 // Then check the global handlers
207 if (ancestor == null) {
208 ancestor = moduleConfig.findExceptionConfig(ancestorType);
209
210 if (ancestor != null) {
211 // If the ancestor is a global handler, set actionConfig
212 // to null so further searches are only done among
213 // global handlers.
214 actionConfig = null;
215 }
216 }
217
218 while (ancestor != null) {
219 // Check if an ancestor is extending *this*
220 if (ancestor == this) {
221 return true;
222 }
223
224 // Get our ancestor's ancestor
225 ancestorType = ancestor.getExtends();
226
227 // check against ancestors extending same typed ancestors
228 if (ancestor.getType().equals(ancestorType)) {
229 // If the ancestor is extending a config for the same type,
230 // make sure we look for its ancestor in the global handlers.
231 // If we're already at that level, we return false.
232 if (actionConfig == null) {
233 return false;
234 } else {
235 // Set actionConfig = null to force us to look for global
236 // forwards
237 actionConfig = null;
238 }
239 }
240
241 ancestor = null;
242
243 // First check the action config
244 if (actionConfig != null) {
245 ancestor = actionConfig.findExceptionConfig(ancestorType);
246 }
247
248 // Then check the global handlers
249 if (ancestor == null) {
250 ancestor = moduleConfig.findExceptionConfig(ancestorType);
251
252 if (ancestor != null) {
253 // Limit further checks to moduleConfig.
254 actionConfig = null;
255 }
256 }
257 }
258
259 return false;
260 }
261
262 // --------------------------------------------------------- Public Methods
263
264 /**
265 * <p>Inherit values that have not been overridden from the provided
266 * config object. Subclasses overriding this method should verify that
267 * the given parameter is of a class that contains a property it is trying
268 * to inherit:</p>
269 *
270 * <pre>
271 * if (config instanceof MyCustomConfig) {
272 * MyCustomConfig myConfig =
273 * (MyCustomConfig) config;
274 *
275 * if (getMyCustomProp() == null) {
276 * setMyCustomProp(myConfig.getMyCustomProp());
277 * }
278 * }
279 * </pre>
280 *
281 * <p>If the given <code>config</code> is extending another object, those
282 * extensions should be resolved before it's used as a parameter to this
283 * method.</p>
284 *
285 * @param config The object that this instance will be inheriting its
286 * values from.
287 * @see #processExtends(ModuleConfig, ActionConfig)
288 */
289 public void inheritFrom(ExceptionConfig config)
290 throws ClassNotFoundException, IllegalAccessException,
291 InstantiationException, InvocationTargetException {
292 if (configured) {
293 throw new IllegalStateException("Configuration is frozen");
294 }
295
296 // Inherit values that have not been overridden
297 if (getBundle() == null) {
298 setBundle(config.getBundle());
299 }
300
301 if (getHandler().equals("org.apache.struts.action.ExceptionHandler")) {
302 setHandler(config.getHandler());
303 }
304
305 if (getKey() == null) {
306 setKey(config.getKey());
307 }
308
309 if (getPath() == null) {
310 setPath(config.getPath());
311 }
312
313 if (getScope().equals("request")) {
314 setScope(config.getScope());
315 }
316
317 if (getType() == null) {
318 setType(config.getType());
319 }
320
321 inheritProperties(config);
322 }
323
324 /**
325 * <p>Inherit configuration information from the ExceptionConfig that this
326 * instance is extending. This method verifies that any exception config
327 * object that it inherits from has also had its processExtends() method
328 * called.</p>
329 *
330 * @param moduleConfig The {@link ModuleConfig} that this config is from.
331 * @param actionConfig The {@link ActionConfig} that this config is from,
332 * if applicable. This must be null for global
333 * forwards.
334 * @see #inheritFrom(ExceptionConfig)
335 */
336 public void processExtends(ModuleConfig moduleConfig,
337 ActionConfig actionConfig)
338 throws ClassNotFoundException, IllegalAccessException,
339 InstantiationException, InvocationTargetException {
340 if (configured) {
341 throw new IllegalStateException("Configuration is frozen");
342 }
343
344 String ancestorType = getExtends();
345
346 if ((!extensionProcessed) && (ancestorType != null)) {
347 ExceptionConfig baseConfig = null;
348
349 // We only check the action config if we're not a global handler
350 boolean checkActionConfig =
351 (this != moduleConfig.findExceptionConfig(getType()));
352
353 // ... and the action config was provided
354 checkActionConfig &= (actionConfig != null);
355
356 // ... and we're not extending a config with the same type value
357 // (because if we are, that means we're an action-level handler
358 // extending a global handler).
359 checkActionConfig &= !ancestorType.equals(getType());
360
361 // We first check in the action config's exception handlers
362 if (checkActionConfig) {
363 baseConfig = actionConfig.findExceptionConfig(ancestorType);
364 }
365
366 // Then check the global exception handlers
367 if (baseConfig == null) {
368 baseConfig = moduleConfig.findExceptionConfig(ancestorType);
369 }
370
371 if (baseConfig == null) {
372 throw new NullPointerException("Unable to find "
373 + "handler for '" + ancestorType + "' to extend.");
374 }
375
376 // Check for circular inheritance and make sure the base config's
377 // own inheritance has been processed already
378 if (checkCircularInheritance(moduleConfig, actionConfig)) {
379 throw new IllegalArgumentException(
380 "Circular inheritance detected for forward " + getType());
381 }
382
383 if (!baseConfig.isExtensionProcessed()) {
384 baseConfig.processExtends(moduleConfig, actionConfig);
385 }
386
387 // copy values from the base config
388 inheritFrom(baseConfig);
389 }
390
391 extensionProcessed = true;
392 }
393
394 /**
395 * Return a String representation of this object.
396 */
397 public String toString() {
398 StringBuffer sb = new StringBuffer("ExceptionConfig[");
399
400 sb.append("type=");
401 sb.append(this.type);
402
403 if (this.bundle != null) {
404 sb.append(",bundle=");
405 sb.append(this.bundle);
406 }
407
408 if (this.inherit != null) {
409 sb.append(",extends=");
410 sb.append(this.inherit);
411 }
412
413 sb.append(",handler=");
414 sb.append(this.handler);
415 sb.append(",key=");
416 sb.append(this.key);
417 sb.append(",path=");
418 sb.append(this.path);
419 sb.append(",scope=");
420 sb.append(this.scope);
421 sb.append("]");
422
423 return (sb.toString());
424 }
425 }