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    }