/* * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.antcontrib.logic; import java.util.Enumeration; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.Sequential; /** * A wrapper that lets you run a set of tasks and optionally run a * different set of tasks if the first set fails and yet another set * after the first one has finished. * * <p>This mirrors Java's try/catch/finally.</p> * * <p>The tasks inside of the required <code><try></code> * element will be run. If one of them should throw a {@link * org.apache.tools.ant.BuildException BuildException} several things * can happen:</p> * * <ul> * <li>If there is no <code><catch></code> block, the * exception will be passed through to Ant.</li> * * <li>If the property attribute has been set, a property of the * given name will be set to the message of the exception.</li> * * <li>If the reference attribute has been set, a reference of the * given id will be created and point to the exception object.</li> * * <li>If there is a <code><catch></code> block, the tasks * nested into it will be run.</li> * </ul> * * <p>If a <code><finally></code> block is present, the task * nested into it will be run, no matter whether the first tasks have * thrown an exception or not.</p> * * <p><strong>Attributes:</strong></p> * * <table> * <tr> * <td>Name</td> * <td>Description</td> * <td>Required</td> * </tr> * <tr> * <td>property</td> * <td>Name of a property that will receive the message of the * exception that has been caught (if any)</td> * <td>No</td> * </tr> * <tr> * <td>reference</td> * <td>Id of a reference that will point to the exception object * that has been caught (if any)</td> * <td>No</td> * </tr> * </table> * * <p>Use the following task to define the <code><trycatch></code> * task before you use it the first time:</p> * * <pre><code> * <taskdef name="trycatch" * classname="net.sf.antcontrib.logic.TryCatchTask" /> * </code></pre> * * <h3>Crude Example</h3> * * <pre><code> * <trycatch property="foo" reference="bar"> * <try> * <fail>Tada!</fail> * </try> * * <catch> * <echo>In &lt;catch&gt;.</echo> * </catch> * * <finally> * <echo>In &lt;finally&gt;.</echo> * </finally> * </trycatch> * * <echo>As property: ${foo}</echo> * <property name="baz" refid="bar" /> * <echo>From reference: ${baz}</echo> * </code></pre> * * <p>results in</p> * * <pre><code> * [trycatch] Caught exception: Tada! * [echo] In <catch>. * [echo] In <finally>. * [echo] As property: Tada! * [echo] From reference: Tada! * </code></pre> * * @author <a href="mailto:stefan.bodewig@freenet.de">Stefan Bodewig</a> * @author <a href="mailto:RITCHED2@Nationwide.com">Dan Ritchey</a> */ public class TryCatchTask extends Task { public static final class CatchBlock extends Sequential { private String throwable = BuildException.class.getName(); public CatchBlock() { super(); } public void setThrowable(String throwable) { this.throwable = throwable; } public boolean execute(Throwable t) throws BuildException { try { Class c = Thread.currentThread().getContextClassLoader().loadClass(throwable); if (c.isAssignableFrom(t.getClass())) { execute(); return true; } return false; } catch (ClassNotFoundException e) { throw new BuildException(e); } } } private Sequential tryTasks = null; private Vector catchBlocks = new Vector(); private Sequential finallyTasks = null; private String property = null; private String reference = null; /** * Adds a nested <try> block - one is required, more is * forbidden. */ public void addTry(Sequential seq) throws BuildException { if (tryTasks != null) { throw new BuildException("You must not specify more than one <try>"); } tryTasks = seq; } public void addCatch(CatchBlock cb) { catchBlocks.add(cb); } /** * Adds a nested <finally> block - at most one is allowed. */ public void addFinally(Sequential seq) throws BuildException { if (finallyTasks != null) { throw new BuildException("You must not specify more than one <finally>"); } finallyTasks = seq; } /** * Sets the property attribute. */ public void setProperty(String p) { property = p; } /** * Sets the reference attribute. */ public void setReference(String r) { reference = r; } /** * The heart of the task. */ public void execute() throws BuildException { Throwable thrown = null; if (tryTasks == null) { throw new BuildException("A nested <try> element is required"); } try { tryTasks.perform(); } catch (Throwable e) { if (property != null) { /* * Using setProperty instead of setNewProperty to * be able to compile with Ant < 1.5. */ getProject().setProperty(property, e.getMessage()); } if (reference != null) { getProject().addReference(reference, e); } boolean executed = false; Enumeration blocks = catchBlocks.elements(); while (blocks.hasMoreElements() && ! executed) { CatchBlock cb = (CatchBlock)blocks.nextElement(); executed = cb.execute(e); } if (! executed) { thrown = e; } } finally { if (finallyTasks != null) { finallyTasks.perform(); } } if (thrown != null) { throw new BuildException(thrown); } } }