/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed 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
*
* http://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.napile.idea.thermit.config.execution;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.napile.idea.thermit.ThermitBundle;
import org.napile.idea.thermit.config.AntBuildFile;
import org.napile.idea.thermit.config.AntBuildFileBase;
import org.napile.idea.thermit.config.AntBuildListener;
import org.napile.idea.thermit.config.impl.BuildFileProperty;
import com.intellij.concurrency.JobScheduler;
import com.intellij.execution.CantRunException;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.CommandLineBuilder;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.junit.JUnitProcessHandler;
import com.intellij.execution.junit2.segments.OutputPacketProcessor;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.util.ExecutionErrorDialog;
import com.intellij.history.LocalHistory;
import com.intellij.ide.macro.Macro;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
public final class ExecutionHandler
{
private static final Logger LOG = Logger.getInstance("#com.intellij.thermit.execution.ExecutionHandler");
@NonNls
public static final String PARSER_JAR = "xerces1.jar";
private ExecutionHandler()
{
}
/**
* @param antBuildListener should not be null. Use {@link org.napile.idea.thermit.config.AntBuildListener#NULL}
*/
public static void runBuild(final AntBuildFileBase buildFile, String[] targets, @Nullable final AntBuildMessageView buildMessageViewToReuse, final DataContext dataContext, List<BuildFileProperty> additionalProperties, @NotNull final AntBuildListener antBuildListener)
{
FileDocumentManager.getInstance().saveAllDocuments();
final AntCommandLineBuilder builder = new AntCommandLineBuilder();
final AntBuildMessageView messageView;
final GeneralCommandLine commandLine;
synchronized(builder)
{
Project project = buildFile.getProject();
try
{
builder.setBuildFile(buildFile.getAllOptions(), VfsUtil.virtualToIoFile(buildFile.getVirtualFile()));
builder.calculateProperties(dataContext, additionalProperties);
builder.addTargets(targets);
builder.getCommandLine().setCharset(EncodingProjectManager.getInstance(buildFile.getProject()).getDefaultCharset());
messageView = prepareMessageView(buildMessageViewToReuse, buildFile, targets);
commandLine = CommandLineBuilder.createFromJavaParameters(builder.getCommandLine());
messageView.setBuildCommandLine(commandLine.getCommandLineString());
}
catch(RunCanceledException e)
{
e.showMessage(project, ThermitBundle.message("run.ant.erorr.dialog.title"));
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return;
}
catch(CantRunException e)
{
ExecutionErrorDialog.show(e, ThermitBundle.message("cant.run.ant.erorr.dialog.title"), project);
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return;
}
catch(Macro.ExecutionCancelledException e)
{
antBuildListener.buildFinished(AntBuildListener.ABORTED, 0);
return;
}
catch(Throwable e)
{
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
LOG.error(e);
return;
}
}
final boolean startInBackground = buildFile.isRunInBackground();
new Task.Backgroundable(null, ThermitBundle.message("ant.build.progress.dialog.title"), true)
{
public boolean shouldStartInBackground()
{
return startInBackground;
}
public void run(@NotNull final ProgressIndicator indicator)
{
try
{
runBuild(indicator, messageView, buildFile, antBuildListener, commandLine);
}
catch(Throwable e)
{
LOG.error(e);
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
}
}
}.queue();
}
private static void runBuild(final ProgressIndicator progress, @NotNull final AntBuildMessageView errorView, @NotNull final AntBuildFile buildFile, @NotNull final AntBuildListener antBuildListener, @NotNull GeneralCommandLine commandLine)
{
final Project project = buildFile.getProject();
final long startTime = System.currentTimeMillis();
LocalHistory.getInstance().putSystemLabel(project, ThermitBundle.message("ant.build.local.history.label", buildFile.getName()));
final JUnitProcessHandler handler;
try
{
handler = JUnitProcessHandler.runCommandLine(commandLine);
}
catch(final ExecutionException e)
{
ApplicationManager.getApplication().invokeLater(new Runnable()
{
public void run()
{
ExecutionErrorDialog.show(e, ThermitBundle.message("could.not.start.process.erorr.dialog.title"), project);
}
});
antBuildListener.buildFinished(AntBuildListener.FAILED_TO_RUN, 0);
return;
}
processRunningAnt(progress, handler, errorView, buildFile, startTime, antBuildListener);
handler.waitFor();
}
private static void processRunningAnt(final ProgressIndicator progress, final JUnitProcessHandler handler, final AntBuildMessageView errorView, final AntBuildFile buildFile, final long startTime, final AntBuildListener antBuildListener)
{
final Project project = buildFile.getProject();
final StatusBar statusbar = WindowManager.getInstance().getStatusBar(project);
if(statusbar != null)
{
statusbar.setInfo(ThermitBundle.message("ant.build.started.status.message"));
}
final CheckCancelTask checkCancelTask = new CheckCancelTask(progress, handler);
checkCancelTask.start(0);
final OutputParser parser = OutputParser2.attachParser(project, handler, errorView, progress, buildFile);
handler.addProcessListener(new ProcessAdapter()
{
public void processTerminated(ProcessEvent event)
{
final long buildTime = System.currentTimeMillis() - startTime;
checkCancelTask.cancel();
parser.setStopped(true);
final OutputPacketProcessor dispatcher = handler.getErr().getEventsDispatcher();
errorView.buildFinished(progress != null && progress.isCanceled(), buildTime, antBuildListener, dispatcher);
ApplicationManager.getApplication().invokeLater(new Runnable()
{
public void run()
{
if(project.isDisposed())
{
return;
}
errorView.removeProgressPanel();
ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
if(toolWindow != null)
{ // can be null if project is closed
toolWindow.activate(null, false);
}
}
}, ModalityState.NON_MODAL);
}
});
handler.startNotify();
}
static final class CheckCancelTask implements Runnable
{
private final ProgressIndicator myProgressIndicator;
private final OSProcessHandler myProcessHandler;
private volatile boolean myCanceled;
public CheckCancelTask(ProgressIndicator progressIndicator, OSProcessHandler process)
{
myProgressIndicator = progressIndicator;
myProcessHandler = process;
}
public void cancel()
{
myCanceled = true;
}
public void run()
{
if(!myCanceled)
{
try
{
myProgressIndicator.checkCanceled();
start(50);
}
catch(ProcessCanceledException e)
{
myProcessHandler.destroyProcess();
}
}
}
public void start(final long delay)
{
JobScheduler.getScheduler().schedule(this, delay, TimeUnit.MILLISECONDS);
}
}
private static AntBuildMessageView prepareMessageView(@Nullable AntBuildMessageView buildMessageViewToReuse, AntBuildFileBase buildFile, String[] targets) throws RunCanceledException
{
AntBuildMessageView messageView;
if(buildMessageViewToReuse != null)
{
messageView = buildMessageViewToReuse;
messageView.emptyAll();
}
else
{
messageView = AntBuildMessageView.openBuildMessageView(buildFile.getProject(), buildFile, targets);
if(messageView == null)
{
throw new RunCanceledException(ThermitBundle.message("canceled.by.user.error.message"));
}
}
return messageView;
}
}