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