package com.intellij.lang.javascript.flex.actions; import com.intellij.flex.FlexCommonUtils; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.lang.javascript.flex.actions.airpackage.AdtPackageTask; import com.intellij.lang.javascript.flex.actions.airpackage.AirPackageProjectParameters; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; import com.intellij.util.TimeoutUtil; import com.intellij.util.text.StringTokenizer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; public abstract class ExternalTask { private static final Logger LOG = Logger.getInstance(ExternalTask.class.getName()); protected final Project myProject; protected final Sdk myFlexSdk; private Process myProcess; private boolean myFinished; private String myCommandLine = ""; protected List<String> myMessages = new ArrayList<>(); private int myExitCode = -1; public ExternalTask(final Project project, final Sdk flexSdk) { myProject = project; myFlexSdk = flexSdk; } public void start() { final List<String> command = createCommandLine(); for (String s : command) { if (s == null) { LOG.error(StringUtil.join(command, s1 -> s1 == null ? "null" : s1, " ")); } } final ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.redirectErrorStream(true); processBuilder.directory(getProcessDir()); myCommandLine = StringUtil.join(command, " "); debug("Executing task: " + myCommandLine); try { myProcess = processBuilder.start(); scheduleInputStreamReading(); } catch (IOException e) { myFinished = true; myMessages.add(e.getMessage()); } } @Nullable protected File getProcessDir() { return null; } private void debug(final String message) { LOG.debug("[" + hashCode() + "] " + message); } protected abstract List<String> createCommandLine(); protected boolean checkMessages() { return myMessages.isEmpty(); } public boolean isFinished() { return myFinished; } public void cancel() { if (myProcess != null) { myProcess.destroy(); try { myExitCode = myProcess.exitValue(); debug("Process complete with exit code " + myExitCode); } catch (IllegalThreadStateException e) {/*ignore*/} } myFinished = true; } public String getCommandLine() { return myCommandLine; } public List<String> getMessages() { return myMessages; } public int getExitCode() { return myExitCode; } protected Process getProcess() { return myProcess; } protected void scheduleInputStreamReading() { ApplicationManager.getApplication().executeOnPooledThread(() -> { boolean usageStarted = false; final InputStreamReader reader = FlexCommonUtils.createInputStreamReader(myProcess.getInputStream()); try { char[] buf = new char[4096]; int read; while ((read = reader.read(buf, 0, buf.length)) >= 0) { final String output = new String(buf, 0, read); debug("Process output: " + output); if (!usageStarted) { final StringTokenizer tokenizer = new StringTokenizer(output, "\r\n"); while (tokenizer.hasMoreElements()) { final String message = tokenizer.nextElement(); if (!StringUtil.isEmptyOrSpaces(message)) { if (message.trim().toLowerCase().startsWith("usage:")) { usageStarted = true; break; } if (message.trim().endsWith("password:")) { final OutputStream outputStream = myProcess.getOutputStream(); outputStream.write("\n".getBytes()); outputStream.flush(); } else { myMessages.add(message); } } } } } } catch (IOException e) { myMessages.add(e.getMessage()); } finally { cancel(); try { reader.close(); } catch (IOException e) {/*ignore*/} } }); } public static boolean runWithProgress(final ExternalTask task, final String progressTitle, final String frameTitle) { return ProgressManager.getInstance().runProcessWithProgressSynchronously(createRunnable(task), progressTitle, true, task.myProject) && checkMessages(task, frameTitle); } public static void runInBackground(final ExternalTask task, final String progressTitle, final @Nullable Consumer<List<String>> onSuccess, final @Nullable Consumer<List<String>> onFailure) { ProgressManager.getInstance().run(new Task.Backgroundable(task.myProject, progressTitle, true) { public void run(@NotNull final ProgressIndicator indicator) { createRunnable(task).run(); } public void onSuccess() { if (task.checkMessages()) { if (onSuccess != null) { ApplicationManager.getApplication().invokeLater(() -> onSuccess.consume(task.getMessages())); } } else if (onFailure != null) { ApplicationManager.getApplication().invokeLater(() -> onFailure.consume(task.getMessages())); } } }); } private static boolean checkMessages(final ExternalTask task, final String frameTitle) { final List<String> messages = task.getMessages(); if (task.checkMessages()) { return true; } else { String message = messages.isEmpty() ? FlexBundle.message("unexpected.empty.adt.output") : StringUtil.join(messages, "\n"); if (message.length() > 10000) { message = message.substring(0, 10000) + "..."; } if (task instanceof AdtPackageTask) { message += "\n\nADT command line:\n" + task.getCommandLine(); } Messages .showIdeaMessageDialog(task.myProject, message, frameTitle, new String[]{Messages.OK_BUTTON}, 0, Messages.getErrorIcon(), null); } return false; } private static Runnable createRunnable(final ExternalTask task) { return () -> { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.setIndeterminate(true); } try { AirPackageProjectParameters.getInstance(task.myProject).setPackagingInProgress(true); task.start(); while (!task.isFinished()) { if (indicator != null && indicator.isCanceled()) { task.cancel(); break; } TimeoutUtil.sleep(200); } } finally { AirPackageProjectParameters.getInstance(task.myProject).setPackagingInProgress(false); } }; } }