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.commons.chain.impl;
018
019
020 import java.util.Collection;
021 import java.util.Iterator;
022 import org.apache.commons.chain.Chain;
023 import org.apache.commons.chain.Command;
024 import org.apache.commons.chain.Context;
025 import org.apache.commons.chain.Filter;
026
027
028 /**
029 * <p>Convenience base class for {@link Chain} implementations.</p>
030 *
031 * @author Craig R. McClanahan
032 * @version $Revision: 480477 $ $Date: 2006-11-29 08:34:52 +0000 (Wed, 29 Nov 2006) $
033 */
034
035 public class ChainBase implements Chain {
036
037
038 // ----------------------------------------------------------- Constructors
039
040
041 /**
042 * <p>Construct a {@link Chain} with no configured {@link Command}s.</p>
043 */
044 public ChainBase() {
045
046 }
047
048
049 /**
050 * <p>Construct a {@link Chain} configured with the specified
051 * {@link Command}.</p>
052 *
053 * @param command The {@link Command} to be configured
054 *
055 * @exception IllegalArgumentException if <code>command</code>
056 * is <code>null</code>
057 */
058 public ChainBase(Command command) {
059
060 addCommand(command);
061
062 }
063
064
065 /**
066 * <p>Construct a {@link Chain} configured with the specified
067 * {@link Command}s.</p>
068 *
069 * @param commands The {@link Command}s to be configured
070 *
071 * @exception IllegalArgumentException if <code>commands</code>,
072 * or one of the individual {@link Command} elements,
073 * is <code>null</code>
074 */
075 public ChainBase(Command[] commands) {
076
077 if (commands == null) {
078 throw new IllegalArgumentException();
079 }
080 for (int i = 0; i < commands.length; i++) {
081 addCommand(commands[i]);
082 }
083
084 }
085
086
087 /**
088 * <p>Construct a {@link Chain} configured with the specified
089 * {@link Command}s.</p>
090 *
091 * @param commands The {@link Command}s to be configured
092 *
093 * @exception IllegalArgumentException if <code>commands</code>,
094 * or one of the individual {@link Command} elements,
095 * is <code>null</code>
096 */
097 public ChainBase(Collection commands) {
098
099 if (commands == null) {
100 throw new IllegalArgumentException();
101 }
102 Iterator elements = commands.iterator();
103 while (elements.hasNext()) {
104 addCommand((Command) elements.next());
105 }
106
107 }
108
109
110 // ----------------------------------------------------- Instance Variables
111
112
113 /**
114 * <p>The list of {@link Command}s configured for this {@link Chain}, in
115 * the order in which they may delegate processing to the remainder of
116 * the {@link Chain}.</p>
117 */
118 protected Command[] commands = new Command[0];
119
120
121 /**
122 * <p>Flag indicating whether the configuration of our commands list
123 * has been frozen by a call to the <code>execute()</code> method.</p>
124 */
125 protected boolean frozen = false;
126
127
128 // ---------------------------------------------------------- Chain Methods
129
130
131 /**
132 * See the {@link Chain} JavaDoc.
133 *
134 * @param command The {@link Command} to be added
135 *
136 * @exception IllegalArgumentException if <code>command</code>
137 * is <code>null</code>
138 * @exception IllegalStateException if no further configuration is allowed
139 */
140 public void addCommand(Command command) {
141
142 if (command == null) {
143 throw new IllegalArgumentException();
144 }
145 if (frozen) {
146 throw new IllegalStateException();
147 }
148 Command[] results = new Command[commands.length + 1];
149 System.arraycopy(commands, 0, results, 0, commands.length);
150 results[commands.length] = command;
151 commands = results;
152
153 }
154
155
156 /**
157 * See the {@link Chain} JavaDoc.
158 *
159 * @param context The {@link Context} to be processed by this
160 * {@link Chain}
161 *
162 * @throws Exception if thrown by one of the {@link Command}s
163 * in this {@link Chain} but not handled by a <code>postprocess()</code>
164 * method of a {@link Filter}
165 * @throws IllegalArgumentException if <code>context</code>
166 * is <code>null</code>
167 *
168 * @return <code>true</code> if the processing of this {@link Context}
169 * has been completed, or <code>false</code> if the processing
170 * of this {@link Context} should be delegated to a subsequent
171 * {@link Command} in an enclosing {@link Chain}
172 */
173 public boolean execute(Context context) throws Exception {
174
175 // Verify our parameters
176 if (context == null) {
177 throw new IllegalArgumentException();
178 }
179
180 // Freeze the configuration of the command list
181 frozen = true;
182
183 // Execute the commands in this list until one returns true
184 // or throws an exception
185 boolean saveResult = false;
186 Exception saveException = null;
187 int i = 0;
188 int n = commands.length;
189 for (i = 0; i < n; i++) {
190 try {
191 saveResult = commands[i].execute(context);
192 if (saveResult) {
193 break;
194 }
195 } catch (Exception e) {
196 saveException = e;
197 break;
198 }
199 }
200
201 // Call postprocess methods on Filters in reverse order
202 if (i >= n) { // Fell off the end of the chain
203 i--;
204 }
205 boolean handled = false;
206 boolean result = false;
207 for (int j = i; j >= 0; j--) {
208 if (commands[j] instanceof Filter) {
209 try {
210 result =
211 ((Filter) commands[j]).postprocess(context,
212 saveException);
213 if (result) {
214 handled = true;
215 }
216 } catch (Exception e) {
217 // Silently ignore
218 }
219 }
220 }
221
222 // Return the exception or result state from the last execute()
223 if ((saveException != null) && !handled) {
224 throw saveException;
225 } else {
226 return (saveResult);
227 }
228
229 }
230
231
232 // -------------------------------------------------------- Package Methods
233
234
235 /**
236 * <p>Return an array of the configured {@link Command}s for this
237 * {@link Chain}. This method is package private, and is used only
238 * for the unit tests.</p>
239 */
240 Command[] getCommands() {
241
242 return (commands);
243
244 }
245
246
247 }