/*
   * <p>
   * An required extension is missing, if an ExtensionInstallationProvider is
   * registered, delegate the installation of that particular extension to it.
   * </p>
   *
   * @param reqInfo Missing extension information
   * @param instInfo Older installed version information
   *
   * @return true if the installation is successful
   */
  protected boolean installExtension(ExtensionInfo reqInfo, ExtensionInfo instInfo)
      throws ExtensionInstallationException {

    Vector currentProviders;
    synchronized (providers) {
      currentProviders = (Vector) providers.clone();
    }
    for (Enumeration e = currentProviders.elements(); e.hasMoreElements(); ) {
      ExtensionInstallationProvider eip = (ExtensionInstallationProvider) e.nextElement();

      if (eip != null) {
        // delegate the installation to the provider
        if (eip.installExtension(reqInfo, instInfo)) {
          debug(reqInfo.name + " installation successful");
          Launcher.ExtClassLoader cl =
              (Launcher.ExtClassLoader) Launcher.getLauncher().getClassLoader().getParent();
          addNewExtensionsToClassLoader(cl);
          return true;
        }
      }
    }
    // We have tried all of our providers, noone could install this
    // extension, we just return failure at this point
    debug(reqInfo.name + " installation failed");
    return false;
  }
  /*
   * <p>
   * Check that a particular dependency on an extension is satisfied.
   * </p>
   * @param extensionName is the key used for the attributes in the manifest
   * @param attr is the attributes of the manifest file
   *
   * @return true if the dependency is satisfied by the installed extensions
   */
  protected boolean checkExtension(final String extensionName, final Attributes attr)
      throws ExtensionInstallationException {
    debug("Checking extension " + extensionName);
    if (checkExtensionAgainstInstalled(extensionName, attr)) return true;

    debug("Extension not currently installed ");
    ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
    return installExtension(reqInfo, null);
  }
  /*
   * Check for all declared required extensions in the jar file
   * manifest.
   */
  protected boolean checkExtensions(JarFile jar) throws ExtensionInstallationException {
    Manifest man;
    try {
      man = jar.getManifest();
    } catch (IOException e) {
      return false;
    }

    if (man == null) {
      // The applet does not define a manifest file, so
      // we just assume all dependencies are satisfied.
      return true;
    }

    boolean result = true;
    Attributes attr = man.getMainAttributes();
    if (attr != null) {
      // Let's get the list of declared dependencies
      String value = attr.getValue(Name.EXTENSION_LIST);
      if (value != null) {
        StringTokenizer st = new StringTokenizer(value);
        // Iterate over all declared dependencies
        while (st.hasMoreTokens()) {
          String extensionName = st.nextToken();
          debug("The file " + jar.getName() + " appears to depend on " + extensionName);
          // Sanity Check
          String extName = extensionName + "-" + Name.EXTENSION_NAME.toString();
          if (attr.getValue(extName) == null) {
            debug(
                "The jar file "
                    + jar.getName()
                    + " appers to depend on "
                    + extensionName
                    + " but does not define the "
                    + extName
                    + " attribute in its manifest ");

          } else {
            if (!checkExtension(extensionName, attr)) {
              debug("Failed installing " + extensionName);
              result = false;
            }
          }
        }
      } else {
        debug("No dependencies for " + jar.getName());
      }
    }
    return result;
  }
 /*
  * <p>
  * Scan the directories and return all files installed in those
  * </p>
  * @param dirs list of directories to scan
  *
  * @return the list of files installed in all the directories
  */
 private static File[] getExtFiles(File[] dirs) throws IOException {
   Vector urls = new Vector();
   for (int i = 0; i < dirs.length; i++) {
     String[] files = dirs[i].list(new JarFilter());
     debug("getExtFiles files.length " + files.length);
     if (files != null) {
       for (int j = 0; j < files.length; j++) {
         File f = new File(dirs[i], files[j]);
         urls.add(f);
         debug("getExtFiles f[" + j + "] " + f);
       }
     }
   }
   File[] ua = new File[urls.size()];
   urls.copyInto(ua);
   debug("getExtFiles ua.length " + ua.length);
   return ua;
 }
 /*
  * <p>
  * Add the newly installed jar file to the extension class loader.
  * </p>
  *
  * @param cl the current installed extension class loader
  *
  * @return true if successful
  */
 private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) {
   try {
     File[] installedExts = getInstalledExtensions();
     for (int i = 0; i < installedExts.length; i++) {
       final File instFile = installedExts[i];
       URL instURL =
           (URL)
               AccessController.doPrivileged(
                   new PrivilegedAction() {
                     public Object run() {
                       try {
                         return (URL) ParseUtil.fileToEncodedURL(instFile);
                       } catch (MalformedURLException e) {
                         debugException(e);
                         return null;
                       }
                     }
                   });
       if (instURL != null) {
         URL[] urls = cl.getURLs();
         boolean found = false;
         for (int j = 0; j < urls.length; j++) {
           debug("URL[" + j + "] is " + urls[j] + " looking for " + instURL);
           if (urls[j].toString().compareToIgnoreCase(instURL.toString()) == 0) {
             found = true;
             debug("Found !");
           }
         }
         if (!found) {
           debug("Not Found ! adding to the classloader " + instURL);
           cl.addExtURL(instURL);
         }
       }
     }
   } catch (MalformedURLException e) {
     e.printStackTrace();
   } catch (IOException e) {
     e.printStackTrace();
     // let's continue with the next installed extension
   }
   return Boolean.TRUE;
 }
  /** @return the java.ext.dirs property as a list of directory */
  private static File[] getExtDirs() {
    String s =
        (String)
            java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction("java.ext.dirs"));

    File[] dirs;
    if (s != null) {
      StringTokenizer st = new StringTokenizer(s, File.pathSeparator);
      int count = st.countTokens();
      debug("getExtDirs count " + count);
      dirs = new File[count];
      for (int i = 0; i < count; i++) {
        dirs[i] = new File(st.nextToken());
        debug("getExtDirs dirs[" + i + "] " + dirs[i]);
      }
    } else {
      dirs = new File[0];
      debug("getExtDirs dirs " + dirs);
    }
    debug("getExtDirs dirs.length " + dirs.length);
    return dirs;
  }
  /**
   * Checks the dependencies of the jar file on installed extension.
   *
   * @param jarFile containing the attriutes declaring the dependencies
   */
  public static synchronized boolean checkExtensionsDependencies(JarFile jar) {
    if (providers == null) {
      // no need to bother, nobody is registered to install missing
      // extensions
      return true;
    }

    try {
      ExtensionDependency extDep = new ExtensionDependency();
      return extDep.checkExtensions(jar);
    } catch (ExtensionInstallationException e) {
      debug(e.getMessage());
    }
    return false;
  }
  /*
   * <p>
   * Check if the requested extension described by the attributes
   * in the manifest under the key extensionName is compatible with
   * the jar file.
   * </p>
   *
   * @param extensionName key in the attibute list
   * @param attr manifest file attributes
   * @param file installed extension jar file to compare the requested
   * extension against.
   */
  protected boolean checkExtensionAgainst(String extensionName, Attributes attr, final File file)
      throws IOException, FileNotFoundException, ExtensionInstallationException {

    debug("Checking extension " + extensionName + " against " + file.getName());

    // Load the jar file ...
    Manifest man;
    try {
      man =
          (Manifest)
              AccessController.doPrivileged(
                  new PrivilegedExceptionAction() {
                    public Object run() throws IOException, FileNotFoundException {
                      if (!file.exists()) throw new FileNotFoundException(file.getName());
                      JarFile jarFile = new JarFile(file);
                      return jarFile.getManifest();
                    }
                  });
    } catch (PrivilegedActionException e) {
      if (e.getException() instanceof FileNotFoundException)
        throw (FileNotFoundException) e.getException();
      throw (IOException) e.getException();
    }

    // Construct the extension information object
    ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
    debug("Requested Extension : " + reqInfo);

    int isCompatible = ExtensionInfo.INCOMPATIBLE;
    ExtensionInfo instInfo = null;

    if (man != null) {
      Attributes instAttr = man.getMainAttributes();
      if (instAttr != null) {
        instInfo = new ExtensionInfo(null, instAttr);
        debug("Extension Installed " + instInfo);
        isCompatible = instInfo.isCompatibleWith(reqInfo);
        switch (isCompatible) {
          case ExtensionInfo.COMPATIBLE:
            debug("Extensions are compatible");
            return true;

          case ExtensionInfo.INCOMPATIBLE:
            debug("Extensions are incompatible");
            return false;

          default:
            // everything else
            debug("Extensions require an upgrade or vendor switch");
            return installExtension(reqInfo, instInfo);
        }
      }
    }
    return true;
  }