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.chain.commands;
22  
23  import org.apache.commons.chain.Catalog;
24  import org.apache.commons.chain.CatalogFactory;
25  import org.apache.commons.chain.Command;
26  import org.apache.commons.chain.Filter;
27  import org.apache.struts.chain.contexts.ActionContext;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  /**
32   * Intercept any exception thrown by a subsequent {@code Command} in this
33   * processing chain, and fire the configured exception handler chain after
34   * storing the exception that has occurred into the {@code Context}.
35   */
36  public class ExceptionCatcher extends ActionCommandBase implements Filter<ActionContext> {
37  
38      /**
39       * The {@code Log} instance for this class.
40       */
41      private final Logger log =
42          LoggerFactory.getLogger(ExceptionCatcher.class);
43  
44      // ------------------------------------------------------ Instance Variables
45  
46      /**
47       * Field for CatalogName property.
48       */
49      private String catalogName = null;
50  
51      /**
52       * Field for ExceptionCommand property.
53       */
54      private String exceptionCommand = null;
55  
56      // -------------------------------------------------------------- Properties
57  
58      /**
59       * Return the name of the {@code Catalog} in which to perform lookups, or
60       * {@code null} for the default {@code Catalog}.
61       *
62       * @return Name of catalog to use, or null
63       */
64      public String getCatalogName() {
65          return (this.catalogName);
66      }
67  
68      /**
69       * Set the name of the {@code Catalog} in which to perform lookups, or
70       * {@code null} for the default {@code Catalog}.
71       *
72       * @param catalogName The new catalog name or {@code null}
73       */
74      public void setCatalogName(String catalogName) {
75          this.catalogName = catalogName;
76      }
77  
78      /**
79       * Return the name of the command to be executed if an exception occurs.
80       *
81       * @return The name of the command to be executed on an exception
82       */
83      public String getExceptionCommand() {
84          return (this.exceptionCommand);
85      }
86  
87      /**
88       * Set the name of the command to be executed if an exception occurs.
89       *
90       * @param exceptionCommand The name of the chain to be executed
91       */
92      public void setExceptionCommand(String exceptionCommand) {
93          this.exceptionCommand = exceptionCommand;
94      }
95  
96      // ---------------------------------------------------------- Public Methods
97  
98      /**
99       * Clear any existing stored exception and pass the {@code context} on to
100      * the remainder of the current chain.
101      *
102      * @param actionCtx The {@code Context} for the current request
103      *
104      * @return {@code false} so that processing continues
105      *
106      * @throws Exception On any error
107      */
108     @Override
109     protected boolean execute_(ActionContext actionCtx)
110         throws Exception {
111         actionCtx.setException(null);
112 
113         return CONTINUE_PROCESSING;
114     }
115 
116     /**
117      * If an exception was thrown by a subsequent {@code Command}, pass it on
118      * to the specified exception handling chain. Otherwise, do nothing.
119      *
120      * @param actionCtx The {@link ActionContext} to be processed by this
121      *                  {@link Filter}
122      * @param exception The {@code Exception} (if any) that was thrown by the
123      *                  last {@link Command} that was executed; otherwise
124      *                  {@code null}
125      *
126      * @return TRUE if post processing an exception occurred and the exception
127      *         processing chain invoked
128      *
129      * @throws IllegalStateException If exception throws exception
130      */
131     public boolean postprocess(ActionContext actionCtx, Exception exception) {
132         // Do nothing if there was no exception thrown
133         if (exception == null) {
134             return (false);
135         }
136 
137         // Stash the exception in the specified context attribute
138         log.debug("Attempting to handle a thrown exception");
139 
140         actionCtx.setException(exception);
141 
142         // Execute the specified command
143         try {
144             Command<ActionContext> command = lookupExceptionCommand();
145 
146             if (command == null) {
147                 log.error("Cannot find exceptionCommand '{}'",
148                     exceptionCommand);
149                 throw new IllegalStateException(
150                     "Cannot find exceptionCommand '" + exceptionCommand + "'");
151             }
152 
153             log.trace("Calling exceptionCommand '{}'", exceptionCommand);
154 
155             command.execute(actionCtx);
156         } catch (Exception e) {
157             log.warn("Exception from exceptionCommand '{}'",
158                 exceptionCommand, e);
159             IllegalStateException e2 = new IllegalStateException("Exception chain threw exception");
160             e2.initCause(e);
161             throw e2;
162         }
163 
164         return true;
165     }
166 
167     /**
168      * Return the command to be executed if an exception occurs.
169      *
170      * @return The command to be executed if an exception occurs
171      *
172      * @throws IllegalArgumentException If catalog cannot be found
173      * @throws IllegalStateException    If command property is not specified
174      */
175     protected Command<ActionContext> lookupExceptionCommand() {
176         final String catalogName = getCatalogName();
177 
178         final CatalogFactory<ActionContext> catalogFactory =
179                 CatalogFactory.getInstance();
180 
181         final Catalog<ActionContext> catalog;
182 
183         if (catalogName == null) {
184             catalog = catalogFactory.getCatalog();
185 
186             if (catalog == null) {
187                 log.error("Cannot find default catalog");
188                 throw new IllegalArgumentException(
189                     "Cannot find default catalog");
190             }
191         } else {
192             catalog = catalogFactory.getCatalog(catalogName);
193 
194             if (catalog == null) {
195                 log.error("Cannot find catalog '{}'", catalogName);
196                 throw new IllegalArgumentException("Cannot find catalog '"
197                     + catalogName + "'");
198             }
199         }
200 
201         String exceptionCommand = getExceptionCommand();
202 
203         if (exceptionCommand == null) {
204             log.error("No exceptionCommand property specified");
205             throw new IllegalStateException(
206                 "No exceptionCommand property specfied");
207         }
208 
209         return catalog.getCommand(exceptionCommand);
210     }
211 }