Home » apache-ant-1.7.1-src » org.apache.tools » ant » taskdefs » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    *
   17    */
   18   
   19   package org.apache.tools.ant.taskdefs;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.io.InputStream;
   24   import java.net.URL;
   25   import java.util.Map;
   26   import java.util.HashMap;
   27   import java.util.Enumeration;
   28   import java.util.Locale;
   29   import java.util.NoSuchElementException;
   30   import java.util.Properties;
   31   
   32   import org.apache.tools.ant.AntTypeDefinition;
   33   import org.apache.tools.ant.ComponentHelper;
   34   import org.apache.tools.ant.BuildException;
   35   import org.apache.tools.ant.Project;
   36   import org.apache.tools.ant.ProjectHelper;
   37   import org.apache.tools.ant.MagicNames;
   38   import org.apache.tools.ant.util.FileUtils;
   39   import org.apache.tools.ant.types.EnumeratedAttribute;
   40   
   41   /**
   42    * Base class for Taskdef and Typedef - handles all
   43    * the attributes for Typedef. The uri and class
   44    * handling is handled by DefBase
   45    *
   46    * @since Ant 1.4
   47    */
   48   public abstract class Definer extends DefBase {
   49   
   50       /**
   51        * the extension of an antlib file for autoloading.
   52        * {@value[
   53        */
   54       private static final String ANTLIB_XML = "/antlib.xml";
   55   
   56       private static class ResourceStack extends ThreadLocal {
   57           public Object initialValue() {
   58               return new HashMap();
   59           }
   60           Map getStack() {
   61               return (Map) get();
   62           }
   63       }
   64       private static ResourceStack resourceStack = new ResourceStack();
   65       private String name;
   66       private String classname;
   67       private File file;
   68       private String resource;
   69   
   70       private   int    format = Format.PROPERTIES;
   71       private   boolean definerSet = false;
   72       private   int         onError = OnError.FAIL;
   73       private   String      adapter;
   74       private   String      adaptTo;
   75   
   76       private   Class       adapterClass;
   77       private   Class       adaptToClass;
   78   
   79       /**
   80        * Enumerated type for onError attribute
   81        *
   82        * @see EnumeratedAttribute
   83        */
   84       public static class OnError extends EnumeratedAttribute {
   85           /** Enumerated values */
   86           public static final int  FAIL = 0, REPORT = 1, IGNORE = 2, FAIL_ALL = 3;
   87   
   88           /**
   89            * text value of onerror option {@value}
   90            */
   91           public static final String POLICY_FAIL = "fail";
   92           /**
   93            * text value of onerror option {@value}
   94            */
   95           public static final String POLICY_REPORT = "report";
   96           /**
   97            * text value of onerror option {@value}
   98            */
   99           public static final String POLICY_IGNORE = "ignore";
  100           /**
  101            * text value of onerror option {@value}
  102            */
  103           public static final String POLICY_FAILALL = "failall";
  104   
  105           /**
  106            * Constructor
  107            */
  108           public OnError() {
  109               super();
  110           }
  111   
  112           /**
  113            * Constructor using a string.
  114            * @param value the value of the attribute
  115            */
  116           public OnError(String value) {
  117               setValue(value);
  118           }
  119   
  120           /**
  121            * get the values
  122            * @return an array of the allowed values for this attribute.
  123            */
  124           public String[] getValues() {
  125               return new String[] {POLICY_FAIL, POLICY_REPORT, POLICY_IGNORE, POLICY_FAILALL};
  126           }
  127       }
  128   
  129       /**
  130        * Enumerated type for format attribute
  131        *
  132        * @see EnumeratedAttribute
  133        */
  134       public static class Format extends EnumeratedAttribute {
  135           /** Enumerated values */
  136           public static final int PROPERTIES = 0, XML = 1;
  137   
  138           /**
  139            * get the values
  140            * @return an array of the allowed values for this attribute.
  141            */
  142           public String[] getValues() {
  143               return new String[] {"properties", "xml"};
  144           }
  145       }
  146   
  147       /**
  148        * What to do if there is an error in loading the class.
  149        * <dl>
  150        *   <li>error - throw build exception</li>
  151        *   <li>report - output at warning level</li>
  152        *   <li>ignore - output at debug level</li>
  153        * </dl>
  154        *
  155        * @param onError an <code>OnError</code> value
  156        */
  157       public void setOnError(OnError onError) {
  158           this.onError = onError.getIndex();
  159       }
  160   
  161       /**
  162        * Sets the format of the file or resource
  163        * @param format the enumerated value - xml or properties
  164        */
  165       public void setFormat(Format format) {
  166           this.format = format.getIndex();
  167       }
  168   
  169       /**
  170        * @return the name for this definition
  171        */
  172       public String getName() {
  173           return name;
  174       }
  175   
  176       /**
  177        * @return the file containing definitions
  178        */
  179       public File getFile() {
  180           return file;
  181       }
  182   
  183       /**
  184        * @return the resource containing definitions
  185        */
  186       public String getResource() {
  187           return resource;
  188       }
  189   
  190   
  191       /**
  192        * Run the definition.
  193        *
  194        * @exception BuildException if an error occurs
  195        */
  196       public void execute() throws BuildException {
  197           ClassLoader al = createLoader();
  198   
  199           if (!definerSet) {
  200               //we arent fully defined yet. this is an error unless
  201               //we are in an antlib, in which case the resource name is determined
  202               //automatically.
  203               //NB: URIs in the ant core package will be "" at this point.
  204               if (getURI() == null) {
  205                   throw new BuildException(
  206                           "name, file or resource attribute of "
  207                                   + getTaskName() + " is undefined",
  208                           getLocation());
  209               }
  210   
  211               if (getURI().startsWith(MagicNames.ANTLIB_PREFIX)) {
  212                   //convert the URI to a resource
  213                   String uri1 = getURI();
  214                   setResource(makeResourceFromURI(uri1));
  215               } else {
  216                   throw new BuildException(
  217                           "Only antlib URIs can be located from the URI alone,"
  218                                   + "not the URI " + getURI());
  219               }
  220           }
  221   
  222           if (name != null) {
  223               if (classname == null) {
  224                   throw new BuildException(
  225                       "classname attribute of " + getTaskName() + " element "
  226                       + "is undefined", getLocation());
  227               }
  228               addDefinition(al, name, classname);
  229           } else {
  230               if (classname != null) {
  231                   String msg = "You must not specify classname "
  232                       + "together with file or resource.";
  233                   throw new BuildException(msg, getLocation());
  234               }
  235               Enumeration/*<URL>*/ urls = null;
  236               if (file != null) {
  237                   final URL url = fileToURL();
  238                   if (url == null) {
  239                       return;
  240                   }
  241                   urls = new Enumeration() {
  242                       private boolean more = true;
  243                       public boolean hasMoreElements() {
  244                           return more;
  245                       }
  246                       public Object nextElement() throws NoSuchElementException {
  247                           if (more) {
  248                               more = false;
  249                               return url;
  250                           } else {
  251                               throw new NoSuchElementException();
  252                           }
  253                       }
  254                   };
  255               } else {
  256                   urls = resourceToURLs(al);
  257               }
  258   
  259               while (urls.hasMoreElements()) {
  260                   URL url = (URL) urls.nextElement();
  261   
  262                   int fmt = this.format;
  263                   if (url.toString().toLowerCase(Locale.US).endsWith(".xml")) {
  264                       fmt = Format.XML;
  265                   }
  266   
  267                   if (fmt == Format.PROPERTIES) {
  268                       loadProperties(al, url);
  269                       break;
  270                   } else {
  271                       if (resourceStack.getStack().get(url) != null) {
  272                           log("Warning: Recursive loading of " + url
  273                               + " ignored"
  274                               + " at " + getLocation()
  275                               + " originally loaded at "
  276                               + resourceStack.getStack().get(url),
  277                               Project.MSG_WARN);
  278                       } else {
  279                           try {
  280                               resourceStack.getStack().put(url, getLocation());
  281                               loadAntlib(al, url);
  282                           } finally {
  283                               resourceStack.getStack().remove(url);
  284                           }
  285                       }
  286                   }
  287               }
  288           }
  289       }
  290   
  291       /**
  292        * This is where the logic to map from a URI to an antlib resource
  293        * is kept.
  294        * @param uri the xml namespace uri that to convert.
  295        * @return the name of a resource. It may not exist
  296        */
  297   
  298       public static String makeResourceFromURI(String uri) {
  299           String path = uri.substring(MagicNames.ANTLIB_PREFIX.length());
  300           String resource;
  301           if (path.startsWith("//")) {
  302               //handle new style full paths to an antlib, in which
  303               //all but the forward slashes are allowed.
  304               resource = path.substring("//".length());
  305               if (!resource.endsWith(".xml")) {
  306                   //if we haven't already named an XML file, it gets antlib.xml
  307                   resource = resource + ANTLIB_XML;
  308               }
  309           } else {
  310               //convert from a package to a path
  311               resource = path.replace('.', '/') + ANTLIB_XML;
  312           }
  313           return resource;
  314       }
  315   
  316       /**
  317        * Convert a file to a file: URL.
  318        *
  319        * @return the URL, or null if it isn't valid and the active error policy
  320        * is not to raise a fault
  321        * @throws BuildException if the file is missing/not a file and the
  322        * policy requires failure at this point.
  323        */
  324       private URL fileToURL() {
  325           String message = null;
  326           if (!(file.exists())) {
  327               message = "File " + file + " does not exist";
  328           }
  329           if (message == null && !(file.isFile())) {
  330               message = "File " + file + " is not a file";
  331           }
  332           try {
  333               if (message == null) {
  334                   return file.toURL();
  335               }
  336           } catch (Exception ex) {
  337               message =
  338                   "File " + file + " cannot use as URL: "
  339                   + ex.toString();
  340           }
  341           // Here if there is an error
  342           switch (onError) {
  343               case OnError.FAIL_ALL:
  344                   throw new BuildException(message);
  345               case OnError.FAIL:
  346                   // Fall Through
  347               case OnError.REPORT:
  348                   log(message, Project.MSG_WARN);
  349                   break;
  350               case OnError.IGNORE:
  351                   // log at a lower level
  352                   log(message, Project.MSG_VERBOSE);
  353                   break;
  354               default:
  355                   // Ignore the problem
  356                   break;
  357           }
  358           return null;
  359       }
  360   
  361       private Enumeration/*<URL>*/ resourceToURLs(ClassLoader classLoader) {
  362           Enumeration ret;
  363           try {
  364               ret = classLoader.getResources(resource);
  365           } catch (IOException e) {
  366               throw new BuildException(
  367                   "Could not fetch resources named " + resource,
  368                   e, getLocation());
  369           }
  370           if (!ret.hasMoreElements()) {
  371               String message = "Could not load definitions from resource "
  372                   + resource + ". It could not be found.";
  373               switch (onError) {
  374                   case OnError.FAIL_ALL:
  375                       throw new BuildException(message);
  376                   case OnError.FAIL:
  377                   case OnError.REPORT:
  378                       log(message, Project.MSG_WARN);
  379                       break;
  380                   case OnError.IGNORE:
  381                       log(message, Project.MSG_VERBOSE);
  382                       break;
  383                   default:
  384                       // Ignore the problem
  385                       break;
  386               }
  387           }
  388           return ret;
  389       }
  390   
  391       /**
  392        * Load type definitions as properties from a URL.
  393        *
  394        * @param al the classloader to use
  395        * @param url the url to get the definitions from
  396        */
  397       protected void loadProperties(ClassLoader al, URL url) {
  398           InputStream is = null;
  399           try {
  400               is = url.openStream();
  401               if (is == null) {
  402                   log("Could not load definitions from " + url,
  403                       Project.MSG_WARN);
  404                   return;
  405               }
  406               Properties props = new Properties();
  407               props.load(is);
  408               Enumeration keys = props.keys();
  409               while (keys.hasMoreElements()) {
  410                   name = ((String) keys.nextElement());
  411                   classname = props.getProperty(name);
  412                   addDefinition(al, name, classname);
  413               }
  414           } catch (IOException ex) {
  415               throw new BuildException(ex, getLocation());
  416           } finally {
  417               FileUtils.close(is);
  418           }
  419       }
  420   
  421       /**
  422        * Load an antlib from a URL.
  423        *
  424        * @param classLoader the classloader to use.
  425        * @param url the url to load the definitions from.
  426        */
  427       private void loadAntlib(ClassLoader classLoader, URL url) {
  428           try {
  429               Antlib antlib = Antlib.createAntlib(getProject(), url, getURI());
  430               antlib.setClassLoader(classLoader);
  431               antlib.setURI(getURI());
  432               antlib.execute();
  433           } catch (BuildException ex) {
  434               throw ProjectHelper.addLocationToBuildException(
  435                   ex, getLocation());
  436           }
  437       }
  438   
  439       /**
  440        * Name of the property file  to load
  441        * ant name/classname pairs from.
  442        * @param file the file
  443        */
  444       public void setFile(File file) {
  445           if (definerSet) {
  446               tooManyDefinitions();
  447           }
  448           definerSet = true;
  449           this.file = file;
  450       }
  451   
  452       /**
  453        * Name of the property resource to load
  454        * ant name/classname pairs from.
  455        * @param res the resource to use
  456        */
  457       public void setResource(String res) {
  458           if (definerSet) {
  459               tooManyDefinitions();
  460           }
  461           definerSet = true;
  462           this.resource = res;
  463       }
  464   
  465       /**
  466        * Antlib attribute, sets resource and uri.
  467        * uri is set the antlib value and, resource is set
  468        * to the antlib.xml resource in the classpath.
  469        * For example antlib="antlib:org.acme.bland.cola"
  470        * corresponds to uri="antlib:org.acme.bland.cola"
  471        * resource="org/acme/bland/cola/antlib.xml".
  472        * ASF Bugzilla Bug 31999
  473        * @param antlib the value to set.
  474        */
  475       public void setAntlib(String antlib) {
  476           if (definerSet) {
  477               tooManyDefinitions();
  478           }
  479           if (!antlib.startsWith("antlib:")) {
  480               throw new BuildException(
  481                   "Invalid antlib attribute - it must start with antlib:");
  482           }
  483           setURI(antlib);
  484           this.resource = antlib.substring("antlib:".length()).replace('.', '/')
  485               + "/antlib.xml";
  486           definerSet = true;
  487       }
  488   
  489       /**
  490        * Name of the definition
  491        * @param name the name of the definition
  492        */
  493       public void setName(String name) {
  494           if (definerSet) {
  495               tooManyDefinitions();
  496           }
  497           definerSet = true;
  498           this.name = name;
  499       }
  500   
  501       /**
  502        * Returns the classname of the object we are defining.
  503        * May be <code>null</code>.
  504        * @return the class name
  505        */
  506       public String getClassname() {
  507           return classname;
  508       }
  509   
  510       /**
  511        * The full class name of the object being defined.
  512        * Required, unless file or resource have
  513        * been specified.
  514        * @param classname the name of the class
  515        */
  516       public void setClassname(String classname) {
  517           this.classname = classname;
  518       }
  519   
  520       /**
  521        * Set the class name of the adapter class.
  522        * An adapter class is used to proxy the
  523        * definition class. It is used if the
  524        * definition class is not assignable to
  525        * the adaptto class, or if the adaptto
  526        * class is not present.
  527        *
  528        * @param adapter the name of the adapter class
  529        */
  530   
  531       public void setAdapter(String adapter) {
  532           this.adapter = adapter;
  533       }
  534   
  535       /**
  536        * Set the adapter class.
  537        *
  538        * @param adapterClass the class to use to adapt the definition class
  539        */
  540       protected void setAdapterClass(Class adapterClass) {
  541           this.adapterClass = adapterClass;
  542       }
  543   
  544       /**
  545        * Set the classname of the class that the definition
  546        * must be compatible with, either directly or
  547        * by use of the adapter class.
  548        *
  549        * @param adaptTo the name of the adaptto class
  550        */
  551       public void setAdaptTo(String adaptTo) {
  552           this.adaptTo = adaptTo;
  553       }
  554   
  555       /**
  556        * Set the class for adaptToClass, to be
  557        * used by derived classes, used instead of
  558        * the adaptTo attribute.
  559        *
  560        * @param adaptToClass the class for adapto.
  561        */
  562       protected void setAdaptToClass(Class adaptToClass) {
  563           this.adaptToClass = adaptToClass;
  564       }
  565   
  566   
  567       /**
  568        * Add a definition using the attributes of Definer
  569        *
  570        * @param al the ClassLoader to use
  571        * @param name the name of the definition
  572        * @param classname the classname of the definition
  573        * @exception BuildException if an error occurs
  574        */
  575       protected void addDefinition(ClassLoader al, String name, String classname)
  576           throws BuildException {
  577           Class cl = null;
  578           try {
  579               try {
  580                   name = ProjectHelper.genComponentName(getURI(), name);
  581   
  582                   if (onError != OnError.IGNORE) {
  583                       cl = Class.forName(classname, true, al);
  584                   }
  585   
  586                   if (adapter != null) {
  587                       adapterClass = Class.forName(adapter, true, al);
  588                   }
  589   
  590                   if (adaptTo != null) {
  591                       adaptToClass = Class.forName(adaptTo, true, al);
  592                   }
  593   
  594                   AntTypeDefinition def = new AntTypeDefinition();
  595                   def.setName(name);
  596                   def.setClassName(classname);
  597                   def.setClass(cl);
  598                   def.setAdapterClass(adapterClass);
  599                   def.setAdaptToClass(adaptToClass);
  600                   def.setClassLoader(al);
  601                   if (cl != null) {
  602                       def.checkClass(getProject());
  603                   }
  604                   ComponentHelper.getComponentHelper(getProject())
  605                       .addDataTypeDefinition(def);
  606               } catch (ClassNotFoundException cnfe) {
  607                   String msg = getTaskName() + " class " + classname
  608                       + " cannot be found";
  609                   throw new BuildException(msg, cnfe, getLocation());
  610               } catch (NoClassDefFoundError ncdfe) {
  611                   String msg = getTaskName() + " A class needed by class "
  612                       + classname + " cannot be found: " + ncdfe.getMessage();
  613                   throw new BuildException(msg, ncdfe, getLocation());
  614               }
  615           } catch (BuildException ex) {
  616               switch (onError) {
  617                   case OnError.FAIL_ALL:
  618                   case OnError.FAIL:
  619                       throw ex;
  620                   case OnError.REPORT:
  621                       log(ex.getLocation() + "Warning: " + ex.getMessage(),
  622                           Project.MSG_WARN);
  623                       break;
  624                   default:
  625                       log(ex.getLocation() + ex.getMessage(),
  626                           Project.MSG_DEBUG);
  627               }
  628           }
  629       }
  630   
  631       /**
  632        * handle too many definitions by raising an exception.
  633        * @throws BuildException always.
  634        */
  635       private void tooManyDefinitions() {
  636           throw new BuildException(
  637               "Only one of the attributes name, file and resource"
  638               + " can be set", getLocation());
  639       }
  640   }

Save This Page
Home » apache-ant-1.7.1-src » org.apache.tools » ant » taskdefs » [javadoc | source]