View Javadoc
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>&lt;exception&gt;</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 }