blob: d7ab4c17d72048c4ba67c6d2c0092216a50628df [file] [log] [blame]
/*
* 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
*
* 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.netbeans.modules.maven.execute;
import org.netbeans.modules.maven.execute.cmd.ExecutionEventObject;
import java.awt.Dialog;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.options.OptionsDisplayer;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.project.Project;
import org.netbeans.modules.maven.api.FileUtilities;
import org.netbeans.modules.maven.api.NbMavenProject;
import org.netbeans.modules.maven.api.execute.RunConfig;
import org.netbeans.modules.maven.api.execute.RunUtils;
import static org.netbeans.modules.maven.execute.Bundle.*;
import org.netbeans.modules.maven.execute.ui.RunGoalsPanel;
import org.netbeans.modules.maven.execute.ui.ShowExecutionPanel;
import org.netbeans.modules.maven.options.MavenOptionController;
import org.netbeans.modules.options.java.api.JavaOptions;
import org.netbeans.spi.project.ui.support.BuildExecutionSupport;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.awt.StatusDisplayer;
import org.openide.execution.ExecutorTask;
import org.openide.filesystems.FileObject;
import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages;
import org.openide.util.RequestProcessor;
import org.openide.windows.InputOutput;
import org.openide.windows.OutputListener;
/**
* common code for MAvenExecutors, sharing tabs and actions..
* @author mkleint
*/
public abstract class AbstractMavenExecutor extends OutputTabMaintainer<AbstractMavenExecutor.TabContext> implements MavenExecutor, Cancellable {
public static final class TabContext {
ReRunAction rerun;
ReRunAction rerunDebug;
ResumeAction resume;
ShowOverviewAction overview;
StopAction stop;
OptionsAction options;
@Override protected TabContext clone() {
TabContext c = new TabContext();
c.rerun = rerun;
c.rerunDebug = rerunDebug;
c.resume = resume;
c.overview = overview;
c.stop = stop;
c.options = options;
return c;
}
}
@Override protected Class<TabContext> tabContextType() {
return TabContext.class;
}
protected RunConfig config;
private TabContext tabContext;
private List<String> messages = new ArrayList<String>();
private List<OutputListener> listeners = new ArrayList<OutputListener>();
protected ExecutorTask task;
protected MavenItem item;
protected final Object SEMAPHORE = new Object();
protected AbstractMavenExecutor(RunConfig conf) {
this(conf, new TabContext());
}
AbstractMavenExecutor(RunConfig conf, TabContext tc) {
super(conf.getExecutionName());
config = conf;
this.tabContext = tc == null ? new TabContext() : tc;
}
@Override public final void setTask(ExecutorTask task) {
synchronized (SEMAPHORE) {
this.task = task;
this.item = new MavenItem();
SEMAPHORE.notifyAll();
}
}
@Override public final void addInitialMessage(String line, OutputListener listener) {
messages.add(line);
listeners.add(listener);
}
protected final void processInitialMessage() {
Iterator<String> it1 = messages.iterator();
Iterator<OutputListener> it2 = listeners.iterator();
InputOutput ioput = getInputOutput();
try {
while (it1.hasNext()) {
OutputListener ol = it2.next();
if (ol != null) {
ioput.getErr().println(it1.next(), ol, true);
} else {
ioput.getErr().println(it1.next());
}
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
protected final void actionStatesAtStart() {
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
createNewTabActions();
tabContext.rerun.setEnabled(false);
tabContext.rerunDebug.setEnabled(false);
tabContext.overview.setRoot(null);
tabContext.resume.setFinder(null);
tabContext.stop.setEnabled(true);
}
});
}
protected interface ResumeFromFinder {
@CheckForNull NbMavenProject find(@NonNull Project root);
}
protected final void actionStatesAtFinish(final @NullAllowed ResumeFromFinder resumeFromFinder, final @NullAllowed ExecutionEventObject.Tree root) {
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
createNewTabActions();
tabContext.rerun.setEnabled(true);
tabContext.rerunDebug.setEnabled(true);
tabContext.resume.setFinder(resumeFromFinder);
tabContext.overview.setRoot(root);
tabContext.stop.setEnabled(false);
}
});
}
@Override
protected void reassignAdditionalContext(TabContext tabContext) {
this.tabContext = tabContext;
tabContext.rerun.setConfig(config);
tabContext.rerunDebug.setConfig(config);
tabContext.resume.setConfig(config);
tabContext.stop.setExecutor(this);
tabContext.overview.setExecutor((MavenCommandLineExecutor)this);
tabContext.overview.setRoot(null);
}
@Override protected final TabContext createContext() {
return tabContext.clone();
}
@Override protected Action[] createNewTabActions() {
if (tabContext.rerun == null) {
tabContext.rerun = new ReRunAction(false);
tabContext.rerunDebug = new ReRunAction(true);
tabContext.resume = new ResumeAction();
tabContext.stop = new StopAction();
tabContext.options = new OptionsAction();
tabContext.overview = new ShowOverviewAction();
}
tabContext.rerun.setConfig(config);
tabContext.rerunDebug.setConfig(config);
tabContext.resume.setConfig(config);
tabContext.overview.setExecutor((MavenCommandLineExecutor) this);
tabContext.stop.setExecutor(this);
return new Action[] {
tabContext.rerun,
tabContext.rerunDebug,
tabContext.resume,
tabContext.overview,
tabContext.stop,
tabContext.options,
};
}
class ReRunAction extends AbstractAction {
private RunConfig config;
private boolean debug;
@Messages({
"TXT_Rerun_extra=Re-run with different parameters",
"TXT_Rerun=Re-run the goals.",
"TIP_Rerun_Extra=Re-run with different parameters",
"TIP_Rerun=Re-run the goals."
})
ReRunAction(boolean debug) {
this.debug = debug;
this.putValue(Action.SMALL_ICON, debug ? ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/refreshdebug.png", false) : //NOI18N
ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/refresh.png", false));//NOI18N
putValue(Action.NAME, debug ? TXT_Rerun_extra() : TXT_Rerun());
putValue(Action.SHORT_DESCRIPTION, debug ? TIP_Rerun_Extra() : TIP_Rerun());
setEnabled(false);
}
void setConfig(RunConfig config) {
this.config = config;
}
@Messages("TIT_Run_maven=Run Maven")
@Override public void actionPerformed(ActionEvent e) {
BeanRunConfig newConfig = new BeanRunConfig(config);
if (debug) {
RunGoalsPanel pnl = new RunGoalsPanel();
DialogDescriptor dd = new DialogDescriptor(pnl, TIT_Run_maven());
pnl.readConfig(config);
Object retValue = DialogDisplayer.getDefault().notify(dd);
if (retValue == DialogDescriptor.OK_OPTION) {
pnl.applyValues(newConfig);
} else {
return;
}
}
actionStatesAtStart();
InputOutput inputOutput = getInputOutput();
try {
inputOutput.getOut().reset();
} catch (IOException ex) {
ex.printStackTrace();
}
MavenCommandLineExecutor.executeMaven(newConfig, inputOutput, tabContext);
//TODO the waiting on tasks won't work..
}
}
private static class ResumeAction extends AbstractAction {
private static final RequestProcessor RP = new RequestProcessor(ResumeAction.class);
private RunConfig config;
private ResumeFromFinder finder;
@Messages("TIP_resume=Resume build starting from failed submodule.")
ResumeAction() {
setEnabled(false);
putValue(SMALL_ICON, ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/forward.png", true));
putValue(SHORT_DESCRIPTION, TIP_resume());
}
void setConfig(RunConfig config) {
this.config = config;
}
void setFinder(ResumeFromFinder finder) {
this.finder = finder;
setEnabled(finder != null);
}
@Messages({
"ResumeAction_scanning=Searching for faulty module",
"ResumeAction_could_not_find_module=Could not determine module from which to resume build."
})
@Override public void actionPerformed(ActionEvent e) {
final Project p = config.getProject();
if (p == null) {
setFinder(null);
StatusDisplayer.getDefault().setStatusText(ResumeAction_could_not_find_module());
return;
}
final AtomicReference<Thread> t = new AtomicReference<Thread>();
final ProgressHandle handle = ProgressHandleFactory.createHandle(ResumeAction_scanning(), new Cancellable() {
@Override public boolean cancel() {
Thread _t = t.get();
if (_t != null) {
_t.interrupt();
return true;
} else {
return false;
}
}
});
RP.post(new Runnable() {
@Override public void run() {
t.set(Thread.currentThread());
handle.start();
NbMavenProject nbmp;
try {
nbmp = finder.find(p);
} finally {
handle.finish();
}
t.set(null);
if (nbmp == null || NbMavenProject.isErrorPlaceholder(nbmp.getMavenProject())) {
setFinder(null);
StatusDisplayer.getDefault().setStatusText(ResumeAction_could_not_find_module());
return;
}
File root = config.getExecutionDirectory();
File module = nbmp.getMavenProject().getBasedir();
String rel = root != null && module != null ? FileUtilities.relativizeFile(root, module) : null;
String id = rel != null ? rel : nbmp.getMavenProject().getGroupId() + ':' + nbmp.getMavenProject().getArtifactId();
BeanRunConfig newConfig = new BeanRunConfig(config);
List<String> goals = new ArrayList<String>(config.getGoals());
int rf = goals.indexOf("--resume-from");
if (rf != -1) {
goals.set(rf + 1, id);
} else {
goals.add(0, "--resume-from");
goals.add(1, id);
}
newConfig.setGoals(goals);
RunUtils.executeMaven(newConfig);
}
});
}
}
static class StopAction extends AbstractAction {
private AbstractMavenExecutor exec;
@Messages({
"TXT_Stop_execution=Stop execution",
"TIP_Stop_Execution=Stop the currently executing build"
})
StopAction() {
putValue(Action.SMALL_ICON, ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/stop.png", false)); //NOi18N
putValue(Action.NAME, TXT_Stop_execution());
putValue(Action.SHORT_DESCRIPTION, TIP_Stop_Execution());
setEnabled(false);
}
void setExecutor(AbstractMavenExecutor ex) {
exec = ex;
}
@Override public void actionPerformed(ActionEvent e) {
setEnabled(false);
RequestProcessor.getDefault().post(new Runnable() {
@Override public void run() {
exec.cancel();
}
});
}
}
public static final class OptionsAction extends AbstractAction {
@Messages("LBL_OptionsAction=Maven Settings")
public OptionsAction() {
super(LBL_OptionsAction(), ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/options.png", true));
putValue(Action.SHORT_DESCRIPTION, LBL_OptionsAction());
}
@Override public void actionPerformed(ActionEvent e) {
OptionsDisplayer.getDefault().open(JavaOptions.JAVA + "/" + MavenOptionController.OPTIONS_SUBPATH);
}
}
private static final class ShowOverviewAction extends AbstractAction {
private MavenCommandLineExecutor executor;
private ExecutionEventObject.Tree root;
private Dialog d;
@Messages("LBL_ShowOverviewAction=Show Build Overview")
ShowOverviewAction() {
super(LBL_ShowOverviewAction(), ImageUtilities.loadImageIcon("org/netbeans/modules/maven/execute/ui/buildplangoals.png", true));
putValue(Action.SHORT_DESCRIPTION, LBL_ShowOverviewAction());
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
if (root == null) {
return; //#238704 not clear when this would happen for an enabled action.
}
ShowExecutionPanel panel = new ShowExecutionPanel();
panel.setTreeToDisplay(root, executor != null ? executor.config : null);
DialogDescriptor dd = new DialogDescriptor(panel, "Build execution overview");
dd.setOptions(new Object[] {"Close"});
dd.setClosingOptions(new Object[] {"Close"});
d = DialogDisplayer.getDefault().createDialog(dd);
d.setModal(false);
d.setVisible(true);
}
private void setExecutor(MavenCommandLineExecutor aThis) {
executor = aThis;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (d != null && d.isVisible()) {
d.setVisible(false);
d = null;
}
}
});
}
private void setRoot(final ExecutionEventObject.Tree root) {
this.root = root;
if (SwingUtilities.isEventDispatchThread()) {
setEnabled(root != null);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setEnabled(root != null);
}
});
}
}
}
protected class MavenItem implements BuildExecutionSupport.ActionItem {
@Override public String getDisplayName() {
return config.getTaskDisplayName();
}
@Override public void repeatExecution() {
RunUtils.executeMaven(config);
}
@Override public boolean isRunning() {
return !task.isFinished();
}
@Override public void stopRunning() {
AbstractMavenExecutor.this.cancel();
}
@Override
public String getAction() {
return config.getActionName() != null ? config.getActionName() : "xxx-custom";
}
@Override
public FileObject getProjectDirectory() {
return config.getProject() != null ? config.getProject().getProjectDirectory() : null;
}
@Override
public int hashCode() {
int hash = 5;
hash = 41 * hash + getAction().hashCode();
hash = 41 * hash + getProjectDirectory().hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MavenItem other = (MavenItem) obj;
if (!this.getAction().equals(other.getAction())) {
return false;
}
if (this.getProjectDirectory() != other.getProjectDirectory() && (this.getProjectDirectory() == null || !this.getProjectDirectory().equals(other.getProjectDirectory()))) {
return false;
}
return true;
}
}
}