content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You 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
   *
   *      https://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 org.apache.tools.ant.taskdefs.optional.ejb;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Files;
  import java.util.Arrays;
  import java.util.Hashtable;
  import java.util.List;
  import javax.xml.parsers.SAXParser;
  
  import org.apache.tools.ant.AntClassLoader;
  import org.apache.tools.ant.BuildException;
  import org.apache.tools.ant.Project;
  import org.apache.tools.ant.taskdefs.Java;
  import org.apache.tools.ant.types.Path;
  
  /**
   * The deployment tool to add the jonas specific deployment descriptors to the
   * ejb JAR file. JONAS only requires one additional file jonas-ejb-jar.xml.
   *
   * @version 1.0
   * @see EjbJar#createJonas
   */
  public class JonasDeploymentTool extends GenericDeploymentTool {
  
      /** Public Id of the standard deployment descriptor DTD. */
      protected static final String EJB_JAR_1_1_PUBLIC_ID
          = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
      protected static final String EJB_JAR_2_0_PUBLIC_ID
          = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
  
      /** Public Id of the JOnAS-specific deployment descriptor DTD. */
      protected static final String JONAS_EJB_JAR_2_4_PUBLIC_ID
          = "-//ObjectWeb//DTD JOnAS 2.4//EN";
      protected static final String JONAS_EJB_JAR_2_5_PUBLIC_ID
          = "-//ObjectWeb//DTD JOnAS 2.5//EN";
  
      /** RMI ORB. */
      protected static final String RMI_ORB = "RMI";
  
      /** JEREMIE ORB. */
      protected static final String JEREMIE_ORB = "JEREMIE";
  
      /** DAVID ORB. */
      protected static final String DAVID_ORB = "DAVID";
  
      /**
       * Name of the standard deployment descriptor DTD (these files are stored in
       * the ${JONAS_ROOT}/xml directory).
       */
      protected static final String EJB_JAR_1_1_DTD = "ejb-jar_1_1.dtd";
      protected static final String EJB_JAR_2_0_DTD = "ejb-jar_2_0.dtd";
  
      /**
       * Name of the JOnAS-specific deployment descriptor DTD (these files are
       * stored in the ${JONAS_ROOT}/xml directory).
       */
      protected static final String JONAS_EJB_JAR_2_4_DTD
          = "jonas-ejb-jar_2_4.dtd";
      protected static final String JONAS_EJB_JAR_2_5_DTD
          = "jonas-ejb-jar_2_5.dtd";
  
      /** Default JOnAS deployment descriptor name. */
      protected static final String JONAS_DD = "jonas-ejb-jar.xml";
  
      /** GenIC class name (JOnAS 2.5) */
      protected static final String GENIC_CLASS =
      "org.objectweb.jonas_ejb.genic.GenIC";
  
      /** Old GenIC class name (JOnAS 2.4.x). */
      protected static final String OLD_GENIC_CLASS_1 =
          "org.objectweb.jonas_ejb.tools.GenWholeIC";
  
      /** Old GenIC class name. */
      protected static final String OLD_GENIC_CLASS_2 =
          "org.objectweb.jonas_ejb.tools.GenIC";
  
      /**
       * Filename of the standard EJB descriptor (which is passed to this class
       * from the parent "ejbjar" task). This file is relative to the directory
       * specified by the "srcdir" attribute in the ejbjar task.
       */
      private String descriptorName;
  
      /**
       * Filename of the JOnAS-specific EJB descriptor (which is passed to this
       * class from the parent "ejbjar" task). This file is relative to the
       * directory specified by the "srcdir" attribute in the ejbjar task.
       */
      private String jonasDescriptorName;
  
      /* ------------- */
      /* GenIC options */
      /* ------------- */
  
      /**
       * Temporary output directory used by GenIC.
       */
      private File outputdir;
  
      /**
       * <code>true</code> if the intermediate Java source files generated by
       * GenIC must be deleted or not. The default is <code>false</code>
       */
      private boolean keepgenerated = false;
  
      /**
       * <code>true</code> if the generated source files must not be compiled via
       * the java and rmi compilers. The default is <code>false</code>.
       */
      private boolean nocompil = false;
  
      /**
       * <code>true</code> if the XML deployment descriptors must be parsed
       * without validation. The default is <code>false</code>.
       */
      private boolean novalidation = false;
  
      /**
       * Java compiler to use. The default is the value of
       * <code>build.compiler</code> property.
       */
      private String javac;
  
      /** Options to pass to the java compiler. */
      private String javacopts;
  
      /** Options to pass to the rmi compiler. */
      private String rmicopts;
  
      /**
       * Whether or not the RMI skeleton and stub must be modified to
       * implement the implicit propagation of the security context (the
       * transactional context is always provided). The default is
       * <code>false</code>.
       */
      private boolean secpropag = false;
  
      /**
       * <code>true</code> if the GenIC call must be verbose. The default
       * is <code>false</code>.
       */
      private boolean verbose = false;
  
      /** Additional args to send to GenIC. */
      private String additionalargs;
  
      /* ------------- */
      /* other options */
      /* ------------- */
  
      /** JOnAS root directory. */
      private File jonasroot;
  
      /**
       * <code>true</code> if the generic JAR file used as input to GenIC must be
       * retained. The default is <code>false</code>.
       */
      private boolean keepgeneric = false;
  
      /** Stores the suffix for the JOnAS JAR file. The default is '.jar'. */
      private String suffix = ".jar";
  
      /**
       *  ORB to use (RMI, JEREMIE or DAVID). If omitted, it defaults to the one
       *  present in classpath. If specified, the corresponding JOnAS JAR is
       *  automatically added to the classpath.
       */
      private String orb;
  
      /**
       * <code>true</code> if GenIC must not be run on the EJB JAR.
       * The default is <code>false</code>.
       */
      private boolean nogenic = false;
  
      /* -------------------- */
      /* GenIC options setter */
      /* -------------------- */
  
      /**
       * Sets the <code>keepgenerated</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setKeepgenerated(boolean aBoolean) {
          keepgenerated = aBoolean;
      }
  
      /**
       * Sets the additional arguments.
       *
       * @param aString additional args.
       */
      public void setAdditionalargs(String aString) {
          additionalargs = aString;
      }
  
      /**
       * Sets the <code>nocompil</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setNocompil(boolean aBoolean) {
          nocompil = aBoolean;
      }
  
      /**
       * Sets the <code>novalidation</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setNovalidation(boolean aBoolean) {
          novalidation = aBoolean;
      }
  
      /**
       * Sets the java compiler to use.
       *
       * @param aString the java compiler.
       */
      public void setJavac(String aString) {
          javac = aString;
      }
  
      /**
       * Set the options to pass to the java compiler.
       *
       * @param aString the options.
       */
      public void setJavacopts(String aString) {
          javacopts = aString;
      }
  
      /**
       * Set the options to pass to the rmi compiler.
       *
       * @param aString the options.
       */
      public void setRmicopts(String aString) {
          rmicopts = aString;
      }
  
      /**
       * Sets the <code>secpropag</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setSecpropag(boolean aBoolean) {
          secpropag = aBoolean;
      }
  
      /**
       * Sets the <code>verbose</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setVerbose(boolean aBoolean) {
          verbose = aBoolean;
      }
  
      /* -------------------- */
      /* other options setter */
      /* -------------------- */
  
      /**
       * Set the JOnAS root directory.
       *
       * @param aFile the JOnAS root directory.
       */
      public void setJonasroot(File aFile) {
          jonasroot = aFile;
      }
  
      /**
       * Sets the <code>keepgeneric</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setKeepgeneric(boolean aBoolean) {
          keepgeneric = aBoolean;
      }
  
      /**
       * Sets the jar suffix.
       *
       * @param aString the string to use as the suffix.
       */
      public void setJarsuffix(String aString) {
          suffix = aString;
      }
  
      /**
       * Sets the <code>orb</code> to construct classpath.
       *
       * @param aString 'RMI', 'JEREMIE', or 'DAVID'.
       */
      public void setOrb(String aString) {
          orb = aString;
      }
  
      /**
       * Sets the <code>nogenic</code> flag.
       *
       * @param aBoolean <code>true</code> if the flag must be set.
       */
      public void setNogenic(boolean aBoolean) {
          nogenic = aBoolean;
      }
  
      /* ------------- */
      /* other methods */
      /* ------------- */
  
      /** {@inheritDoc}. */
      @Override
      public void processDescriptor(String aDescriptorName, SAXParser saxParser) {
  
          descriptorName = aDescriptorName;
  
          log("JOnAS Deployment Tool processing: " + descriptorName,
              Project.MSG_VERBOSE);
  
          super.processDescriptor(descriptorName, saxParser);
  
          if (outputdir != null) {
              // the method deleteOnExit() do not work because the directory is not empty
              log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
              deleteAllFiles(outputdir);
          }
      }
  
      /** {@inheritDoc}. */
      @Override
      protected void writeJar(String baseName, File jarfile, Hashtable<String, File> ejbFiles, String publicId)
      throws BuildException {
  
          // create the generic jar first
          File genericJarFile = super.getVendorOutputJarFile(baseName);
          super.writeJar(baseName, genericJarFile, ejbFiles, publicId);
  
          // GenIC call on generic jar
          addGenICGeneratedFiles(genericJarFile, ejbFiles);
  
          // create the real jar
          super.writeJar(baseName, getVendorOutputJarFile(baseName), ejbFiles, publicId);
  
          if (!keepgeneric) {
              log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE);
              genericJarFile.delete();
          }
      }
  
      /** {@inheritDoc}. */
      @Override
      protected void addVendorFiles(Hashtable<String, File> ejbFiles, String ddPrefix) {
  
          // JOnAS-specific descriptor deployment
          jonasDescriptorName = getJonasDescriptorName();
          File jonasDD = new File(getConfig().descriptorDir, jonasDescriptorName);
  
          if (jonasDD.exists()) {
              ejbFiles.put(META_DIR + JONAS_DD, jonasDD);
          } else {
              log("Unable to locate the JOnAS deployment descriptor. It was expected to be in: "
                  + jonasDD.getPath() + ".", Project.MSG_WARN);
          }
      }
  
      /** {@inheritDoc}. */
      @Override
      protected File getVendorOutputJarFile(String baseName) {
          return new File(getDestDir(), baseName + suffix);
      }
  
      /**
       * Determines the name of the JOnAS-specific EJB descriptor using the
       * specified standard EJB descriptor name. In general, the standard
       * descriptor will be named "[basename]-ejb-jar.xml", and this method will
       * return "[basename]-jonas-ejb-jar.xml" or "jonas-[basename].xml"
       *
       * @return The name of the JOnAS-specific EJB descriptor file.
       */
      private String getJonasDescriptorName() {
  
          // descriptorName = <path><basename><basenameterminator><remainder>
          // examples = /org/objectweb/fooAppli/foo/Foo-ejb-jar.xml
          // examples = /org/objectweb/fooAppli/foo/Foo.xml (JOnAS convention)
  
          String jonasDN; // JOnAS-specific DD
          boolean jonasConvention = false; // true if the JOnAS convention is used for the DD
          String path;            // Directory path of the EJB descriptor
          String fileName;        // EJB descriptor file name
          String baseName;        // Filename appearing before name terminator
          String remainder;       // Filename appearing after the name terminator
  
          int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
          if (startOfFileName != -1) {
              // extract path info
              path = descriptorName.substring(0, startOfFileName + 1);
              fileName = descriptorName.substring(startOfFileName + 1);
          } else {
              // descriptorName is just a file without path
              path = "";
              fileName = descriptorName;
          }
  
          if (fileName.startsWith(EJB_DD)) {
              return path + JONAS_DD;
          }
  
          int endOfBaseName = descriptorName.indexOf(getConfig().baseNameTerminator, startOfFileName);
  
          /*
           * Check for the odd case where the terminator and/or filename
           * extension aren't found.  These will ensure "jonas-" appears at the
           * end of the name and before the '.' (if present).
           */
          if (endOfBaseName < 0) {
              // baseNameTerminator not found: the descriptor use the
              // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
              // not [Foo<baseNameTerminator>-ejb-jar.xml,
              // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
              endOfBaseName = descriptorName.lastIndexOf('.') - 1;
              if (endOfBaseName < 0) {
                  // no . found
                  endOfBaseName = descriptorName.length() - 1;
              }
  
              jonasConvention = true;
          }
  
          baseName = descriptorName.substring(startOfFileName + 1, endOfBaseName + 1);
          remainder = descriptorName.substring(endOfBaseName + 1);
  
          if (jonasConvention) {
              jonasDN = path + "jonas-" + baseName + ".xml";
          } else {
              jonasDN = path + baseName + "jonas-" + remainder;
          }
  
          log("Standard EJB descriptor name: " + descriptorName, Project.MSG_VERBOSE);
          log("JOnAS-specific descriptor name: " + jonasDN, Project.MSG_VERBOSE);
  
          return jonasDN;
      }
  
      /** {@inheritDoc}. */
      @Override
      protected String getJarBaseName(String descriptorFileName) {
  
          String baseName = null;
  
          if (getConfig().namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
  
              // try to find JOnAS specific convention name
              if (!descriptorFileName.contains(getConfig().baseNameTerminator)) {
  
                  // baseNameTerminator not found: the descriptor use the
                  // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
                  // not [Foo<baseNameTerminator>-ejb-jar.xml,
                  // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
  
                  String aCanonicalDescriptor = descriptorFileName.replace('\\', '/');
                  int lastSeparatorIndex = aCanonicalDescriptor.lastIndexOf('/');
                  int endOfBaseName;
  
                  if (lastSeparatorIndex != -1) {
                      endOfBaseName = descriptorFileName.indexOf(".xml", lastSeparatorIndex);
                  } else {
                      endOfBaseName = descriptorFileName.indexOf(".xml");
                  }
  
                  if (endOfBaseName != -1) {
                      baseName = descriptorFileName.substring(0, endOfBaseName);
                  }
              }
          }
  
          if (baseName == null) {
              // else get standard baseName
              baseName = super.getJarBaseName(descriptorFileName);
          }
  
          log("JAR base name: " + baseName, Project.MSG_VERBOSE);
  
          return baseName;
      }
  
      /** {@inheritDoc}. */
      @Override
      protected void registerKnownDTDs(DescriptorHandler handler) {
          handler.registerDTD(EJB_JAR_1_1_PUBLIC_ID,
                      jonasroot + File.separator + "xml" + File.separator + EJB_JAR_1_1_DTD);
          handler.registerDTD(EJB_JAR_2_0_PUBLIC_ID,
                      jonasroot + File.separator + "xml" + File.separator + EJB_JAR_2_0_DTD);
  
          handler.registerDTD(JONAS_EJB_JAR_2_4_PUBLIC_ID,
                      jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_4_DTD);
          handler.registerDTD(JONAS_EJB_JAR_2_5_PUBLIC_ID,
                      jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_5_DTD);
      }
  
      /**
       * Add to the given hashtable all the file generated by GenIC.
       *
       * @param genericJarFile jar file.
       * @param ejbFiles the hashtable.
       */
      private void addGenICGeneratedFiles(
          File genericJarFile, Hashtable<String, File> ejbFiles) {
          if (nogenic) {
              return;
          }
  
          Java genicTask = new Java(getTask()); // GenIC task
          genicTask.setTaskName("genic");
          genicTask.setFork(true);
  
          // jonasroot
          genicTask.createJvmarg().setValue("-Dinstall.root=" + jonasroot);
  
          // java policy file
          String jonasConfigDir = jonasroot + File.separator + "config";
          File javaPolicyFile = new File(jonasConfigDir, "java.policy");
          if (javaPolicyFile.exists()) {
              genicTask.createJvmarg().setValue("-Djava.security.policy="
                                + javaPolicyFile.toString());
          }
  
          // outputdir
          try {
              outputdir = createTempDir();
          } catch (IOException aIOException) {
              String msg = "Cannot create temp dir: " + aIOException.getMessage();
              throw new BuildException(msg, aIOException);
          }
          log("Using temporary output directory: " + outputdir, Project.MSG_VERBOSE);
  
          genicTask.createArg().setValue("-d");
          genicTask.createArg().setFile(outputdir);
  
          for (String key : ejbFiles.keySet()) {
              File f = new File(outputdir + File.separator + key);
              f.getParentFile().mkdirs();
          }
          log("Worked around a bug of GenIC 2.5.", Project.MSG_VERBOSE);
  
          // classpath
          Path classpath = getCombinedClasspath();
          if (classpath == null) {
              classpath = new Path(getTask().getProject());
          }
          classpath.append(new Path(classpath.getProject(), jonasConfigDir));
          classpath.append(new Path(classpath.getProject(), outputdir.toString()));
  
          // try to create the classpath for the correct ORB
          if (orb != null) {
              String orbJar = jonasroot + File.separator + "lib"
                  + File.separator + orb + "_jonas.jar";
              classpath.append(new Path(classpath.getProject(), orbJar));
          }
          log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE);
          genicTask.setClasspath(classpath);
  
          String genicClass; // GenIC class (3 are supported for various
          // versions
          // work around a bug of GenIC 2.5
  
          // class name (search in the classpath provided for the ejbjar element)
          genicClass = getGenicClassName(classpath);
          if (genicClass == null) {
              log("Cannot find GenIC class in classpath.", Project.MSG_ERR);
              throw new BuildException("GenIC class not found, please check the classpath.");
          }
          log("Using '" + genicClass + "' GenIC class.", Project.MSG_VERBOSE);
          genicTask.setClassname(genicClass);
  
          // keepgenerated
          if (keepgenerated) {
              genicTask.createArg().setValue("-keepgenerated");
          }
  
          // nocompil
          if (nocompil) {
              genicTask.createArg().setValue("-nocompil");
          }
  
          // novalidation
          if (novalidation) {
              genicTask.createArg().setValue("-novalidation");
          }
  
          // javac
          if (javac != null) {
              genicTask.createArg().setValue("-javac");
              genicTask.createArg().setLine(javac);
          }
  
          // javacopts
          if (javacopts != null && !javacopts.isEmpty()) {
              genicTask.createArg().setValue("-javacopts");
              genicTask.createArg().setLine(javacopts);
          }
  
          // rmicopts
          if (rmicopts != null && !rmicopts.isEmpty()) {
              genicTask.createArg().setValue("-rmicopts");
              genicTask.createArg().setLine(rmicopts);
          }
  
          // secpropag
          if (secpropag) {
              genicTask.createArg().setValue("-secpropag");
          }
  
          // verbose
          if (verbose) {
              genicTask.createArg().setValue("-verbose");
          }
  
          // additionalargs
          if (additionalargs != null) {
              genicTask.createArg().setValue(additionalargs);
          }
  
          // the generated classes must not be added in the generic JAR!
          // is that buggy on old JOnAS (2.4) ??
          genicTask.createArg().setValue("-noaddinjar");
  
          // input file to process by GenIC
          genicTask.createArg().setValue(genericJarFile.getPath());
  
          // calling GenIC task
          log("Calling " + genicClass + " for " + getConfig().descriptorDir
              + File.separator + descriptorName + ".", Project.MSG_VERBOSE);
  
          if (genicTask.executeJava() != 0) {
  
              // the method deleteOnExit() do not work because the directory is not empty
              log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
              deleteAllFiles(outputdir);
  
              if (!keepgeneric) {
                  log("Deleting generic JAR " + genericJarFile.toString(),
                      Project.MSG_VERBOSE);
                  genericJarFile.delete();
              }
  
              throw new BuildException("GenIC reported an error.");
          }
  
          // add the generated files to the ejbFiles
          addAllFiles(outputdir, "", ejbFiles);
      }
  
      /**
       * Get the GenIC class name to use in the given classpath.
       *
       * @param classpath classpath where the GenIC class must be searched.
       * @return the GenIC class name. Return <code>null</code> if the class name
       * cannot be found.
       */
      String getGenicClassName(Path classpath) {
  
          log("Looking for GenIC class in classpath: "
              + classpath.toString(), Project.MSG_VERBOSE);
  
          try (AntClassLoader cl = classpath.getProject().createClassLoader(classpath)) {
  
              try {
                  cl.loadClass(JonasDeploymentTool.GENIC_CLASS);
                  log("Found GenIC class '" + JonasDeploymentTool.GENIC_CLASS
                      + "' in classpath.", Project.MSG_VERBOSE);
                  return JonasDeploymentTool.GENIC_CLASS;
  
              } catch (ClassNotFoundException cnf1) {
                  log("GenIC class '" + JonasDeploymentTool.GENIC_CLASS
                      + "' not found in classpath.",
                      Project.MSG_VERBOSE);
              }
  
              try {
                  cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_1);
                  log("Found GenIC class '"
                      + JonasDeploymentTool.OLD_GENIC_CLASS_1
                      + "' in classpath.", Project.MSG_VERBOSE);
                  return JonasDeploymentTool.OLD_GENIC_CLASS_1;
  
              } catch (ClassNotFoundException cnf2) {
                  log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1
                      + "' not found in classpath.",
                      Project.MSG_VERBOSE);
              }
  
              try {
                  cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_2);
                  log("Found GenIC class '"
                      + JonasDeploymentTool.OLD_GENIC_CLASS_2
                      + "' in classpath.", Project.MSG_VERBOSE);
                  return JonasDeploymentTool.OLD_GENIC_CLASS_2;
  
              } catch (ClassNotFoundException cnf3) {
                  log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2
                      + "' not found in classpath.",
                      Project.MSG_VERBOSE);
              }
          }
          return null;
      }
  
      /**
       * Verify the configuration.
       * @param descriptorFileName the name of the descriptor file.
       * @param saxParser          not used.
       * @throws BuildException if there is an error.
       */
      @Override
      protected void checkConfiguration(String descriptorFileName,
                        SAXParser saxParser) throws BuildException {
  
          // jonasroot
          if (jonasroot == null) {
              throw new BuildException("The jonasroot attribute is not set.");
          }
          if (!jonasroot.isDirectory()) {
              throw new BuildException(
                  "The jonasroot attribute '%s' is not a valid directory.",
                  jonasroot);
          }
  
          // orb
          final List<String> validOrbs =
              Arrays.asList(RMI_ORB, JEREMIE_ORB, DAVID_ORB);
  
          if (orb != null && !validOrbs.contains(orb)) {
              throw new BuildException(
                  "The orb attribute '%s' is not valid (must be one of %s.", orb,
                  validOrbs);
          }
  
          // additionalargs
          if (additionalargs != null && additionalargs.isEmpty()) {
              throw new BuildException("Empty additionalargs attribute.");
          }
  
          // javac
          if (javac != null && javac.isEmpty()) {
              throw new BuildException("Empty javac attribute.");
          }
      }
  
      /* ----------------------------------------------------------------------------------- */
      /* utilitary methods */
      /* ----------------------------------------------------------------------------------- */
  
      /**
       * Create a temporary directory for GenIC output.
       *
       * @return the temp directory.
       * @throws BuildException if a temp directory cannot be created.
       */
      private File createTempDir() throws IOException {
          return Files.createTempDirectory("genic").toFile();
      }
  
      /**
       * Delete a file. If the file is a directory, delete recursively all the
       * files inside.
       *
       * @param aFile file to delete.
       */
      private void deleteAllFiles(File aFile) {
          if (aFile.isDirectory()) {
              for (File child : aFile.listFiles()) {
                  deleteAllFiles(child);
              }
          }
          aFile.delete();
      }
  
      /**
       * Add a file to the a given hashtable. If the file is a directory, add
       * recursivly all the files inside to the hashtable.
       *
       * @param file the file to add.
       * @param rootDir the current sub-directory to scan.
       * @param hashtable the hashtable where to add the files.
       */
      private void addAllFiles(File file, String rootDir, Hashtable<String, File> hashtable) {
          if (!file.exists()) {
              throw new IllegalArgumentException();
          }
          String newRootDir;
          if (file.isDirectory()) {
              for (File child : file.listFiles()) {
                  if (rootDir.isEmpty()) {
                      newRootDir = child.getName();
                  } else {
                      newRootDir = rootDir + File.separator + child.getName();
                  }
                  addAllFiles(child, newRootDir, hashtable);
              }
          } else {
              hashtable.put(rootDir, file);
          }
      }
  }
  