/**
  * Download the remote content and store it the temp directory.
  *
  * @param URLs
  * @param progressMonitor
  */
 public IStatus download(String[] URLs, IProgressMonitor progressMonitor) {
   if (URLs.length == 0) {
     String err = Messages.InstallerConfigurationProcessor_missingDownloadTargets;
     applyErrorAttributes(err);
     IdeLog.logError(
         PortalUIPlugin.getDefault(),
         "We expected an array of URLs, but got an empty array.",
         new Exception(err)); // $NON-NLS-1$
     return new Status(IStatus.ERROR, PortalUIPlugin.PLUGIN_ID, err);
   }
   downloadedPaths = null;
   DownloadManager downloadManager = new DownloadManager();
   List<URI> urlsList = new ArrayList<URI>(URLs.length);
   for (int i = 0; i < URLs.length; i++) {
     try {
       urlsList.add(new URI(urls[i]));
     } catch (URISyntaxException mue) {
       IdeLog.logError(PortalUIPlugin.getDefault(), mue);
     }
   }
   try {
     downloadManager.addURIs(urlsList);
     IStatus status = downloadManager.start(progressMonitor);
     if (status.isOK()) {
       downloadedPaths = downloadManager.getContentsLocations();
     }
     return status;
   } catch (Exception e) {
     IdeLog.logError(PortalUIPlugin.getDefault(), e);
   }
   return Status.CANCEL_STATUS;
 }
  /**
   * Cache the installed application location and version in the preferences.
   *
   * @param installDir - The directory the application was installed to.
   * @param versionedFileLocation - Can be the URL that we grabbed the installer from, or any other
   *     string that contains a version information in a form of x.y.z.
   * @param appName - The application name (e.g. xampp)
   */
  @SuppressWarnings("unchecked")
  public void cacheVersion(String installDir, String versionedFileLocation, String appName) {

    IPreferenceStore preferenceStore = PortalUIPlugin.getDefault().getPreferenceStore();
    String versions = preferenceStore.getString(IPortalPreferences.CACHED_VERSIONS_PROPERTY_NAME);
    Map<String, Map<String, String>> versionsMap = null;
    if (versions == null || versions.equals(StringUtil.EMPTY)) {
      versionsMap = new HashMap<String, Map<String, String>>();
    } else {
      versionsMap = (Map<String, Map<String, String>>) JSON.parse(versions);
    }
    Map<String, String> appVersionMap = new HashMap<String, String>();
    Version version = VersionUtil.parseVersion(versionedFileLocation);
    if (!VersionUtil.isEmpty(version)) {
      appVersionMap.put(IPortalPreferences.CACHED_VERSION_PROPERTY, version.toString());
      appVersionMap.put(IPortalPreferences.CACHED_LOCATION_PROPERTY, installDir);
      versionsMap.put(appName.toLowerCase(), appVersionMap);
      preferenceStore.setValue(
          IPortalPreferences.CACHED_VERSIONS_PROPERTY_NAME, JSON.toString(versionsMap));
    } else {
      IdeLog.logError(
          PortalUIPlugin.getDefault(),
          MessageFormat.format(
              "Could not cache the location and version for {0}. Install dir: {1}, versionedFileLocation: {2}", //$NON-NLS-1$
              appName, installDir, versionedFileLocation),
          new Exception());
    }
  }
 /**
  * Opens an internal browser with the URL that is given in the attributes.
  *
  * @param attributes We expect for an array that contains a single string URL.
  * @return {@link IBrowserNotificationConstants#JSON_OK} or a {@link
  *     IBrowserNotificationConstants#JSON_ERROR}
  */
 @ControllerAction
 public Object internalOpen(Object attributes) {
   URL url = getURL(attributes);
   if (url == null) {
     return IBrowserNotificationConstants.JSON_ERROR;
   }
   try {
     IWebBrowser browser =
         PortalUIPlugin.getDefault()
             .getWorkbench()
             .getBrowserSupport()
             .createBrowser(
                 IWorkbenchBrowserSupport.AS_EDITOR
                     | IWorkbenchBrowserSupport.LOCATION_BAR
                     | IWorkbenchBrowserSupport.STATUS
                     | IWorkbenchBrowserSupport.NAVIGATION_BAR,
                 url.toString(),
                 null,
                 null);
     browser.openURL(url);
   } catch (PartInitException e) {
     PortalUIPlugin.logError(e);
     return IBrowserNotificationConstants.JSON_ERROR;
   }
   return IBrowserNotificationConstants.JSON_OK;
 }
 /**
  * Returns the URL from the attributes. Null, if an error occured.
  *
  * @param attributes
  * @return A URL, or null if an error occurs.
  */
 private URL getURL(Object attributes) {
   if (attributes instanceof Object[]) {
     Object[] arr = (Object[]) attributes;
     if (arr.length == 1) {
       try {
         return new URL(arr[0].toString());
       } catch (MalformedURLException e) {
         PortalUIPlugin.logError("Invalid URL: " + arr[0], e); // $NON-NLS-1$
       }
     } else {
       PortalUIPlugin.logError(
           new Exception(
               "Wrong argument count passed to BrowserActionController::getURL. Expected 1 and got "
                   + arr.length)); //$NON-NLS-1$
     }
   } else {
     PortalUIPlugin.logError(
         new Exception(
             "Wrong argument type passed to BrowserActionController::getURL. Expected Object[] and got " //$NON-NLS-1$
                 + ((attributes == null)
                     ? "null"
                     : attributes.getClass().getName()))); // $NON-NLS-1$s
   }
   return null;
 }
 /**
  * Opens a Studio view.<br>
  *
  * @param attributes We expect for an array that contains a single string Id for the view.
  */
 @ControllerAction
 public Object openView(Object attributes) {
   String viewId = getViewId(attributes);
   if (viewId == null) {
     return IBrowserNotificationConstants.JSON_ERROR;
   }
   try {
     PortalUIPlugin.getDefault()
         .getWorkbench()
         .getActiveWorkbenchWindow()
         .getActivePage()
         .showView(viewId);
   } catch (Exception e) {
     PortalUIPlugin.logError(e);
     return IBrowserNotificationConstants.JSON_ERROR;
   }
   return IBrowserNotificationConstants.JSON_OK;
 }
 /**
  * Returns the view-Id from the attributes. Null, if an error occurred.
  *
  * @param attributes
  * @return A view Id, or null if an error occurs.
  */
 private String getViewId(Object attributes) {
   if (attributes instanceof Object[]) {
     Object[] arr = (Object[]) attributes;
     if (arr.length == 1 && arr[0] != null) {
       return arr[0].toString();
     } else {
       PortalUIPlugin.logError(
           new Exception(
               "Wrong argument count passed to ViewActionController::openView. Expected 1 and got "
                   + arr.length)); //$NON-NLS-1$
     }
   } else {
     PortalUIPlugin.logError(
         new Exception(
             "Wrong argument type passed to ViewActionController::openView. Expected Object[] and got " //$NON-NLS-1$
                 + ((attributes == null)
                     ? "null"
                     : attributes.getClass().getName()))); // $NON-NLS-1$s
   }
   return null;
 }
 /**
  * Finalize the installation by placing a .aptana file in the installed directory, specifying some
  * properties.
  *
  * @param installDir
  */
 protected void finalizeInstallation(String installDir) {
   super.finalizeInstallation(installDir);
   File propertiesFile = new File(installDir, APTANA_PROPERTIES_FILE_NAME);
   Properties properties = new Properties();
   properties.put("PYTHON_install", urls[0]); // $NON-NLS-1$
   FileOutputStream fileOutputStream = null;
   try {
     fileOutputStream = new FileOutputStream(propertiesFile);
     properties.store(
         fileOutputStream, NLS.bind(Messages.InstallProcessor_aptanaInstallationComment, PYTHON));
   } catch (IOException e) {
     IdeLog.logError(PortalUIPlugin.getDefault(), e);
   } finally {
     if (fileOutputStream != null) {
       try {
         fileOutputStream.flush();
         fileOutputStream.close();
       } catch (IOException e) {
       }
     }
   }
 }
 /**
  * Extract the given zip file into the target folder on a Windows machine.
  *
  * @param sfxZip Self extracting 7zip file.
  * @param targetFolder
  * @return The status of that extraction result.
  */
 public static IStatus extractWin(IPath sfxZip, IPath targetFolder) {
   IStatus errorStatus =
       new Status(
           IStatus.ERROR,
           PortalUIPlugin.PLUGIN_ID,
           Messages.InstallerConfigurationProcessor_unableToExtractZip);
   if (!Platform.OS_WIN32.equals(Platform.getOS())) {
     IdeLog.logError(
         PortalUIPlugin.getDefault(),
         "Unable to extract the Zip file. A Windows OS extractor was called for a non-Windows platform.",
         new Exception()); //$NON-NLS-1$
     return errorStatus;
   }
   if (sfxZip == null || targetFolder == null) {
     IdeLog.logError(
         PortalUIPlugin.getDefault(),
         "Undefined zip file or target folder",
         new Exception()); //$NON-NLS-1$
     return errorStatus;
   }
   File destinationFolder = targetFolder.toFile();
   if (!destinationFolder.exists() && !destinationFolder.mkdirs()) {
     IdeLog.logError(
         PortalUIPlugin.getDefault(),
         "Failed to create destination directory " + destinationFolder,
         new Exception()); //$NON-NLS-1$
     return errorStatus;
   }
   // TODO Use ProcessUtil!
   ProcessBuilder processBuilder =
       new ProcessBuilder(
           sfxZip.toOSString(),
           "-o" + targetFolder.toOSString(), // $NON-NLS-1$
           "-y", //$NON-NLS-1$
           sfxZip.toOSString());
   processBuilder.directory(destinationFolder);
   processBuilder.redirectErrorStream(true);
   String output = null;
   try {
     Process process = processBuilder.start();
     InputStreamGobbler errorGobbler =
         new InputStreamGobbler(process.getErrorStream(), "\n", null); // $NON-NLS-1$
     InputStreamGobbler outputGobbler =
         new InputStreamGobbler(process.getInputStream(), "\n", null); // $NON-NLS-1$
     outputGobbler.start();
     errorGobbler.start();
     process.waitFor();
     outputGobbler.interrupt();
     errorGobbler.interrupt();
     outputGobbler.join();
     errorGobbler.join();
     output = outputGobbler.getResult();
     String errors = errorGobbler.getResult();
     int exitVal = process.exitValue();
     if (exitVal == 0) {
       return Status.OK_STATUS;
     }
     IdeLog.logError(
         PortalUIPlugin.getDefault(),
         "Zip extraction failed. The process returned " + exitVal,
         new Exception("Process output:\n" + errors)); // $NON-NLS-1$ //$NON-NLS-2$
     return errorStatus;
   } catch (Exception e) {
     IdeLog.logError(PortalUIPlugin.getDefault(), e);
     return errorStatus;
   } finally {
     if (output != null) {
       IdeLog.logInfo(PortalUIPlugin.getDefault(), output);
     }
   }
 }
  /**
   * Install Python on the machine.<br>
   * The configuration will grab the installer from the given attributes.<br>
   * We expect an array of attributes with the same structure described at {@link
   * #loadAttributes(Object)}.
   *
   * @param attributes First - A string array of size 1, which contains the URL for the Python
   *     installer (.exe). Second - (optional) map of additional attributes.
   * @see
   *     com.aptana.configurations.processor.AbstractConfigurationProcessor#configure(org.eclipse.core.runtime.IProgressMonitor,
   *     java.lang.Object)
   * @see #loadAttributes(Object)
   */
  @Override
  public ConfigurationStatus configure(IProgressMonitor progressMonitor, Object attributes) {
    // Get a Class lock to avoid multiple installations at the same time even with multiple
    // instances of this
    // processor
    synchronized (this.getClass()) {
      if (installationInProgress) {
        return configurationStatus;
      }
      installationInProgress = true;
    }
    if (!Platform.OS_WIN32.equals(Platform.getOS())) {
      String err = "The Python installer processor is designed to work on Windows."; // $NON-NLS-1$
      IdeLog.logError(PortalUIPlugin.getDefault(), new Exception(err));
      applyErrorAttributes(err);
      installationInProgress = false;
      return configurationStatus;
    }
    try {
      configurationStatus.removeAttribute(CONFIG_ATTR);
      clearErrorAttributes();

      // Load the installer's attributes
      IStatus loadingStatus = loadAttributes(attributes);
      if (!loadingStatus.isOK()) {
        String message = loadingStatus.getMessage();
        applyErrorAttributes(message);
        IdeLog.logError(PortalUIPlugin.getDefault(), new Exception(message));
        return configurationStatus;
      }

      // Check that we got the expected single install URL
      if (urls.length != 1) {
        // structure error
        String err =
            NLS.bind(
                Messages.InstallProcessor_wrongNumberOfInstallLinks,
                new Object[] {PYTHON, 1, urls.length});
        applyErrorAttributes(err);
        IdeLog.logError(PortalUIPlugin.getDefault(), new Exception(err));
        return configurationStatus;
      }
      // Try to get the default install directory from the optional attributes
      installDir = attributesMap.get(INSTALL_DIR_ATTRIBUTE);
      if (installDir == null) {
        installDir = PYTHON_DEFAULT_INSTALL_DIR;
      }
      // Start the installation...
      configurationStatus.setStatus(ConfigurationStatus.PROCESSING);
      IStatus status = download(urls, progressMonitor);
      if (status.isOK()) {
        status = install(progressMonitor);
      }
      switch (status.getSeverity()) {
        case IStatus.OK:
        case IStatus.INFO:
        case IStatus.WARNING:
          displayMessageInUIThread(
              MessageDialog.INFORMATION,
              NLS.bind(Messages.InstallProcessor_installerTitle, PYTHON),
              NLS.bind(Messages.InstallProcessor_installationSuccessful, PYTHON));
          configurationStatus.setStatus(ConfigurationStatus.OK);
          break;
        case IStatus.ERROR:
          configurationStatus.setStatus(ConfigurationStatus.ERROR);
          break;
        case IStatus.CANCEL:
          configurationStatus.setStatus(ConfigurationStatus.INCOMPLETE);
          break;
        default:
          configurationStatus.setStatus(ConfigurationStatus.UNKNOWN);
      }
      return configurationStatus;
    } finally {
      synchronized (this.getClass()) {
        installationInProgress = false;
      }
    }
  }
 public PythonInstallerOptionsDialog() {
   super(Display.getDefault().getActiveShell(), PYTHON);
   setTitleImage(
       PortalUIPlugin.getDefault().getImageRegistry().get(PortalUIPlugin.PYTHON_IMAGE));
 }
  /**
   * Run the PYTHON installer and install XMAPP into the given directory.
   *
   * @param installationAttributes - Attributes map that contains the installation directory and a
   *     specification whether to run the PYTHON auto-install script.
   * @return The status of this installation
   */
  protected IStatus installPYTHON(final Map<String, Object> installationAttributes) {
    Job job =
        new Job(
            NLS.bind(
                Messages.InstallProcessor_installerJobName,
                PYTHON + ' ' + Messages.InstallProcessor_installerGroupTitle)) {
          @Override
          protected IStatus run(IProgressMonitor monitor) {
            try {
              // extract the values from the attributes:
              String installDir =
                  (String) installationAttributes.get(InstallerOptionsDialog.INSTALL_DIR_ATTR);
              // This installer requires Windows path slashes style (backslashes)
              installDir = installDir.replaceAll("/", "\\\\"); // $NON-NLS-1$ //$NON-NLS-2$

              SubMonitor subMonitor = SubMonitor.convert(monitor, IProgressMonitor.UNKNOWN);
              subMonitor.beginTask(
                  NLS.bind(Messages.InstallProcessor_installingTaskName, PYTHON),
                  IProgressMonitor.UNKNOWN);
              IdeLog.logInfo(
                  PortalUIPlugin.getDefault(),
                  "Installing Python into " + installDir); // $NON-NLS-1$

              // Try to get a file lock first, before running the process. This file was just
              // downloaded, so there
              // is a chance it's still being held by the OS or by the downloader.
              IStatus fileLockStatus =
                  LockUtils.waitForLockRelease(downloadedPaths.get(0).toOSString(), 10000L);
              if (!fileLockStatus.isOK()) {
                return new Status(
                    IStatus.ERROR,
                    PortalUIPlugin.PLUGIN_ID,
                    NLS.bind(Messages.InstallProcessor_failedToInstallSeeLog, PYTHON));
              }
              // Run the Python installer, as specified in this link:
              // http://www.python.org/download/releases/2.5/msi/
              List<String> command = new ArrayList<String>(4);
              command.add("msiexec"); // $NON-NLS-1$
              command.add("/i"); // $NON-NLS-1$
              command.add(downloadedPaths.get(0).toOSString());
              command.add("/qr"); // $NON-NLS-1$
              command.add("TARGETDIR=\"" + installDir + '\"'); // $NON-NLS-1$
              if (Boolean.FALSE.toString().equals(attributesMap.get(INSTALL_FOR_ALL_USERS_ATTR))) {
                command.add("ALLUSERS=0"); // $NON-NLS-1$
              } else {
                command.add("ALLUSERS=1"); // $NON-NLS-1$
              }
              ProcessBuilder processBuilder = new ProcessBuilder(command);
              Process process = processBuilder.start();
              int res = process.waitFor();
              if (res == PYTHON_INSTALLER_PROCESS_CANCEL_CODE) {
                IdeLog.logInfo(
                    PortalUIPlugin.getDefault(), "Python installation cancelled"); // $NON-NLS-1$
                return Status.CANCEL_STATUS;
              }
              if (res != 0) {
                // We had an error while installing
                IdeLog.logError(
                    PortalUIPlugin.getDefault(),
                    "Failed to install Python. The PYTHON installer process returned a termination code of "
                        + res); //$NON-NLS-1$
                return new Status(
                    IStatus.ERROR,
                    PortalUIPlugin.PLUGIN_ID,
                    res,
                    NLS.bind(Messages.InstallProcessor_installationErrorMessage, PYTHON, PYTHON),
                    null);
              } else if (!new File(installDir).exists()) {
                // Just to be sure that we got everything in place
                IdeLog.logError(
                    PortalUIPlugin.getDefault(),
                    "Failed to install Python. The " + installDir + " directory was not created",
                    (Throwable) null); // $NON-NLS-1$ //$NON-NLS-2$
                return new Status(
                    IStatus.ERROR,
                    PortalUIPlugin.PLUGIN_ID,
                    res,
                    NLS.bind(Messages.InstallProcessor_installationError_installDirMissing, PYTHON),
                    null);
              }

              finalizeInstallation(installDir);
              return Status.OK_STATUS;
            } catch (Exception e) {
              IdeLog.logError(PortalUIPlugin.getDefault(), e.getMessage(), e);
              return new Status(
                  IStatus.ERROR,
                  PortalUIPlugin.PLUGIN_ID,
                  NLS.bind(Messages.InstallProcessor_failedToInstallSeeLog, PYTHON),
                  e);
            } finally {
              monitor.done();
            }
          }
        };
    // Give it a little delay, just in case the downloader still holds on to the installer file.
    job.schedule(1000);
    try {
      job.join();
    } catch (InterruptedException e) {
      IdeLog.logError(PortalUIPlugin.getDefault(), e.getMessage(), e);
      return Status.CANCEL_STATUS;
    }
    return job.getResult();
  }
  /**
   * Do the PYTHON installation.
   *
   * @param progressMonitor
   * @return A status indication of the process success or failure.
   */
  protected IStatus install(IProgressMonitor progressMonitor) {
    if (CollectionsUtil.isEmpty(downloadedPaths)) {
      String failureMessge = Messages.InstallProcessor_couldNotLocateInstaller;
      String err = NLS.bind(Messages.InstallProcessor_failedToInstall, PYTHON);
      displayMessageInUIThread(
          MessageDialog.ERROR,
          Messages.InstallProcessor_installationErrorTitle,
          err + ' ' + failureMessge);
      return new Status(IStatus.ERROR, PortalUIPlugin.PLUGIN_ID, err + ' ' + failureMessge);
    }
    SubMonitor subMonitor =
        SubMonitor.convert(
            progressMonitor,
            Messages.InstallProcessor_installerProgressInfo,
            IProgressMonitor.UNKNOWN);
    final Map<String, Object> installationAttributes = new HashMap<String, Object>();
    try {
      subMonitor.beginTask(
          NLS.bind(Messages.InstallProcessor_installingTaskName, PYTHON), IProgressMonitor.UNKNOWN);
      final String[] installDir = new String[1];
      Job installPythonDialog = new UIJob("Python installer options") // $NON-NLS-1$
          {
            @Override
            public IStatus runInUIThread(IProgressMonitor monitor) {
              PythonInstallerOptionsDialog dialog = new PythonInstallerOptionsDialog();
              if (dialog.open() == Window.OK) {
                installationAttributes.putAll(dialog.getAttributes());
                return Status.OK_STATUS;
              }
              return Status.CANCEL_STATUS;
            }
          };
      installPythonDialog.schedule();
      try {
        installPythonDialog.join();
      } catch (InterruptedException e) {
      }
      IStatus result = installPythonDialog.getResult();
      if (!result.isOK()) {
        return result;
      }

      IStatus status = installPYTHON(installationAttributes);
      if (!status.isOK()) {
        return status;
      }
      IdeLog.logInfo(
          PortalUIPlugin.getDefault(),
          MessageFormat.format(
              "Successfully installed PYTHON into {0}. PYTHON installation completed.",
              installDir[0])); // $NON-NLS-1$
      // note that we called the finalizeInstallation from the installPYTHON Job.
      return Status.OK_STATUS;
    } catch (Exception e) {
      IdeLog.logError(
          PortalUIPlugin.getDefault(), "Error while installing PYTHON", e); // $NON-NLS-1$
      return new Status(
          IStatus.ERROR,
          PortalUIPlugin.PLUGIN_ID,
          NLS.bind(Messages.InstallProcessor_errorWhileInstalling, PYTHON));
    } finally {
      subMonitor.done();
    }
  }