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.generic; 018 019 020 import org.apache.commons.chain.Catalog; 021 import org.apache.commons.chain.CatalogFactory; 022 import org.apache.commons.chain.Command; 023 import org.apache.commons.chain.Context; 024 import org.apache.commons.chain.Filter; 025 026 027 /** 028 * <p>Look up a specified {@link Command} (which could also be a 029 * {@link org.apache.commons.chain.Chain}) 030 * in a {@link Catalog}, and delegate execution to it. If the delegated-to 031 * {@link Command} is also a {@link Filter}, its <code>postprocess()</code> 032 * method will also be invoked at the appropriate time.</p> 033 * 034 * <p>The name of the {@link Command} can be specified either directly (via 035 * the <code>name</code> property) or indirectly (via the <code>nameKey</code> 036 * property). Exactly one of these must be set.</p> 037 * 038 * <p>If the <code>optional</code> property is set to <code>true</code>, 039 * failure to find the specified command in the specified catalog will be 040 * silently ignored. Otherwise, a lookup failure will trigger an 041 * <code>IllegalArgumentException</code>.</p> 042 * 043 * @author Craig R. McClanahan 044 * @version $Revision: 532951 $ $Date: 2007-04-27 04:59:00 +0100 (Fri, 27 Apr 2007) $ 045 */ 046 047 public class LookupCommand implements Filter { 048 049 050 // -------------------------------------------------------------- Constructors 051 052 /** 053 * Create an instance, setting its <code>catalogFactory</code> property to the 054 * value of <code>CatalogFactory.getInstance()</code>. 055 * 056 * @since Chain 1.1 057 */ 058 public LookupCommand() { 059 this(CatalogFactory.getInstance()); 060 } 061 062 /** 063 * Create an instance and initialize the <code>catalogFactory</code> property 064 * to given <code>factory</code>/ 065 * 066 * @param factory The Catalog Factory. 067 * 068 * @since Chain 1.1 069 */ 070 public LookupCommand(CatalogFactory factory) { 071 this.catalogFactory = factory; 072 } 073 074 075 // -------------------------------------------------------------- Properties 076 077 private CatalogFactory catalogFactory = null; 078 079 /** 080 * <p>Set the {@link CatalogFactory} from which lookups will be 081 * performed.</p> 082 * 083 * @param catalogFactory The Catalog Factory. 084 * 085 * @since Chain 1.1 086 */ 087 public void setCatalogFactory(CatalogFactory catalogFactory) { 088 this.catalogFactory = catalogFactory; 089 } 090 091 /** 092 * Return the {@link CatalogFactory} from which lookups will be performed. 093 * @return The Catalog factory. 094 * 095 * @since Chain 1.1 096 */ 097 public CatalogFactory getCatalogFactory() { 098 099 return this.catalogFactory; 100 } 101 102 103 private String catalogName = null; 104 105 /** 106 * <p>Return the name of the {@link Catalog} to be searched, or 107 * <code>null</code> to search the default {@link Catalog}.</p> 108 * @return The Catalog name. 109 */ 110 public String getCatalogName() { 111 112 return (this.catalogName); 113 114 } 115 116 117 /** 118 * <p>Set the name of the {@link Catalog} to be searched, or 119 * <code>null</code> to search the default {@link Catalog}.</p> 120 * 121 * @param catalogName The new {@link Catalog} name or <code>null</code> 122 */ 123 public void setCatalogName(String catalogName) { 124 125 this.catalogName = catalogName; 126 127 } 128 129 130 private String name = null; 131 132 133 /** 134 * <p>Return the name of the {@link Command} that we will look up and 135 * delegate execution to.</p> 136 * @return The name of the Command. 137 */ 138 public String getName() { 139 140 return (this.name); 141 142 } 143 144 145 /** 146 * <p>Set the name of the {@link Command} that we will look up and 147 * delegate execution to.</p> 148 * 149 * @param name The new command name 150 */ 151 public void setName(String name) { 152 153 this.name = name; 154 155 } 156 157 158 private String nameKey = null; 159 160 161 /** 162 * <p>Return the context attribute key under which the {@link Command} 163 * name is stored.</p> 164 * @return The context key of the Command. 165 */ 166 public String getNameKey() { 167 168 return (this.nameKey); 169 170 } 171 172 173 /** 174 * <p>Set the context attribute key under which the {@link Command} 175 * name is stored.</p> 176 * 177 * @param nameKey The new context attribute key 178 */ 179 public void setNameKey(String nameKey) { 180 181 this.nameKey = nameKey; 182 183 } 184 185 186 private boolean optional = false; 187 188 189 /** 190 * <p>Return <code>true</code> if locating the specified command 191 * is optional.</p> 192 * @return <code>true</code> if the Command is optional. 193 */ 194 public boolean isOptional() { 195 196 return (this.optional); 197 198 } 199 200 201 /** 202 * <p>Set the optional flag for finding the specified command.</p> 203 * 204 * @param optional The new optional flag 205 */ 206 public void setOptional(boolean optional) { 207 208 this.optional = optional; 209 210 } 211 212 private boolean ignoreExecuteResult = false; 213 214 /** 215 * <p>Return <code>true</code> if this command should ignore 216 * the return value from executing the looked-up command. 217 * Defaults to <code>false</code>, which means that the return result 218 * of executing this lookup will be whatever is returned from that 219 * command.</p> 220 * @return <code>true</code> if result of the looked up Command 221 * should be ignored. 222 * 223 * @since Chain 1.1 224 */ 225 public boolean isIgnoreExecuteResult() { 226 return ignoreExecuteResult; 227 } 228 229 /** 230 * <p>Set the rules for whether or not this class will ignore or 231 * pass through the value returned from executing the looked up 232 * command.</p> 233 * <p>If you are looking up a chain which may be "aborted" and 234 * you do not want this class to stop chain processing, then this 235 * value should be set to <code>true</code></p> 236 * @param ignoreReturn <code>true</code> if result of the 237 * looked up Command should be ignored. 238 * 239 * @since Chain 1.1 240 */ 241 public void setIgnoreExecuteResult(boolean ignoreReturn) { 242 this.ignoreExecuteResult = ignoreReturn; 243 } 244 245 private boolean ignorePostprocessResult = false; 246 247 /** 248 * <p>Return <code>true</code> if this command is a Filter and 249 * should ignore the return value from executing the looked-up Filter's 250 * <code>postprocess()</code> method. 251 * Defaults to <code>false</code>, which means that the return result 252 * of executing this lookup will be whatever is returned from that 253 * Filter.</p> 254 * @return <code>true</code> if result of the looked up Filter's 255 * <code>postprocess()</code> method should be ignored. 256 * 257 * @since Chain 1.1 258 */ 259 public boolean isIgnorePostprocessResult() { 260 return ignorePostprocessResult; 261 } 262 263 /** 264 * <p>Set the rules for whether or not this class will ignore or 265 * pass through the value returned from executing the looked up 266 * Filter's <code>postprocess()</code> method.</p> 267 * <p>If you are looking up a Filter which may be "aborted" and 268 * you do not want this class to stop chain processing, then this 269 * value should be set to <code>true</code></p> 270 * @param ignorePostprocessResult <code>true</code> if result of the 271 * looked up Filter's <code>postprocess()</code> method should be ignored. 272 * 273 * @since Chain 1.1 274 */ 275 public void setIgnorePostprocessResult(boolean ignorePostprocessResult) { 276 this.ignorePostprocessResult = ignorePostprocessResult; 277 } 278 // ---------------------------------------------------------- Filter Methods 279 280 281 /** 282 * <p>Look up the specified command, and (if found) execute it. 283 * Unless <code>ignoreExecuteResult</code> is set to <code>true</code>, 284 * return the result of executing the found command. If no command 285 * is found, return <code>false</code>, unless the <code>optional</code> 286 * property is <code>false</code>, in which case an <code>IllegalArgumentException</code> 287 * will be thrown. 288 * </p> 289 * 290 * @param context The context for this request 291 * 292 * @exception IllegalArgumentException if no such {@link Command} 293 * can be found and the <code>optional</code> property is set 294 * to <code>false</code> 295 * @return the result of executing the looked-up command, or 296 * <code>false</code> if no command is found or if the command 297 * is found but the <code>ignoreExecuteResult</code> property of this 298 * instance is <code>true</code> 299 * @throws Exception if and error occurs in the looked-up Command. 300 */ 301 public boolean execute(Context context) throws Exception { 302 303 Command command = getCommand(context); 304 if (command != null) { 305 boolean result = (command.execute(context)); 306 if (isIgnoreExecuteResult()) { 307 return false; 308 } 309 return result; 310 } else { 311 return (false); 312 } 313 314 } 315 316 317 /** 318 * <p>If the executed command was itself a {@link Filter}, call the 319 * <code>postprocess()</code> method of that {@link Filter} as well.</p> 320 * 321 * @param context The context for this request 322 * @param exception Any <code>Exception</code> thrown by command execution 323 * 324 * @return the result of executing the <code>postprocess</code> method 325 * of the looked-up command, unless <code>ignorePostprocessResult</code> is 326 * <code>true</code>. If no command is found, return <code>false</code>, 327 * unless the <code>optional</code> property is <code>false</code>, in which 328 * case <code>IllegalArgumentException</code> will be thrown. 329 */ 330 public boolean postprocess(Context context, Exception exception) { 331 332 Command command = getCommand(context); 333 if (command != null) { 334 if (command instanceof Filter) { 335 boolean result = (((Filter) command).postprocess(context, exception)); 336 if (isIgnorePostprocessResult()) { 337 return false; 338 } 339 return result; 340 } 341 } 342 return (false); 343 344 } 345 346 347 // --------------------------------------------------------- Private Methods 348 349 350 /** 351 * <p>Return the {@link Catalog} to look up the {@link Command} in.</p> 352 * 353 * @param context {@link Context} for this request 354 * @return The catalog. 355 * @exception IllegalArgumentException if no {@link Catalog} 356 * can be found 357 * 358 * @since Chain 1.2 359 */ 360 protected Catalog getCatalog(Context context) { 361 CatalogFactory lookupFactory = this.catalogFactory; 362 if (lookupFactory == null) { 363 lookupFactory = CatalogFactory.getInstance(); 364 } 365 366 String catalogName = getCatalogName(); 367 Catalog catalog = null; 368 if (catalogName == null) { 369 // use default catalog 370 catalog = lookupFactory.getCatalog(); 371 } else { 372 catalog = lookupFactory.getCatalog(catalogName); 373 } 374 if (catalog == null) { 375 if (catalogName == null) { 376 throw new IllegalArgumentException 377 ("Cannot find default catalog"); 378 } else { 379 throw new IllegalArgumentException 380 ("Cannot find catalog '" + catalogName + "'"); 381 } 382 } 383 384 return catalog; 385 } 386 387 /** 388 * <p>Return the {@link Command} instance to be delegated to.</p> 389 * 390 * @param context {@link Context} for this request 391 * @return The looked-up Command. 392 * @exception IllegalArgumentException if no such {@link Command} 393 * can be found and the <code>optional</code> property is set 394 * to <code>false</code> 395 */ 396 protected Command getCommand(Context context) { 397 398 Catalog catalog = getCatalog(context); 399 400 Command command = null; 401 String name = getCommandName(context); 402 if (name != null) { 403 command = catalog.getCommand(name); 404 if ((command == null) && !isOptional()) { 405 if (catalogName == null) { 406 throw new IllegalArgumentException 407 ("Cannot find command '" + name 408 + "' in default catalog"); 409 } else { 410 throw new IllegalArgumentException 411 ("Cannot find command '" + name 412 + "' in catalog '" + catalogName + "'"); 413 } 414 } 415 return (command); 416 } else { 417 throw new IllegalArgumentException("No command name"); 418 } 419 420 } 421 422 /** 423 * <p>Return the name of the {@link Command} instance to be delegated to.</p> 424 * 425 * @param context {@link Context} for this request 426 * @return The name of the {@link Command} instance 427 * 428 * @since Chain 1.2 429 */ 430 protected String getCommandName(Context context) { 431 432 String name = getName(); 433 if (name == null) { 434 name = (String) context.get(getNameKey()); 435 } 436 return name; 437 438 } 439 440 }