——————————————————————————————————————————————————————————————————————
Let's step through the code required for this functionality. First, I have extended org.apache.tools.ant.module.spi.AntLogger, which is exposed in META-INF/services, in a file called "org.apache.tools.ant.module.spi.AntLogger":
public class StoreDeployedApps extends org.apache.tools.ant.module.spi.AntLogger {
@Override
public boolean interestedInSession(AntSession session) {
return true;
}
@Override
public boolean interestedInAllScripts(AntSession session) {
return true;
}
@Override
public String[] interestedInTargets(AntSession session) {
return AntLogger.ALL_TARGETS;
}
@Override
public void targetStarted(AntEvent event) {
BufferedReader br = null;
try {
//Get the build-impl.xml:
File buildImplXML = event.getScriptLocation();
//From the build-impl.xml parent, get the project.xml:
File projectXML = new File(buildImplXML.getParentFile(), "project.xml");
String targetName = event.getTargetName();
String projectName = null;
String projectType = null;
//Read the Dom and figure out the project name and type,
//where the type is a string like "org.netbeans.modules.java.j2seproject":
InputSource source = new InputSource(new FileInputStream(projectXML));
org.w3c.dom.Document doc = XMLUtil.parse(source, false, false, null, null);
org.w3c.dom.NodeList nodeList = doc.getElementsByTagName("\*");
int length = nodeList.getLength();
for (int i = 0; i < length; i++) {
org.w3c.dom.Node currentNode = nodeList.item(i);
String nodeName = currentNode.getNodeName();
if ("name".equals(nodeName)){
projectName = currentNode.getTextContent();
}
if ("type".equals(nodeName)){
projectType = currentNode.getTextContent();
}
}
//If the target name is run, send the build-impl.xml, the project name,
//and the project type to our action class:
if (targetName.equals("run")) {
ListDeployedAppsAction.setProjectNames(buildImplXML, projectName, projectType);
}
} catch (SAXException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} finally {
try {
br.close();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
@Override
public void targetFinished(AntEvent event) {
}
}
And here's the action class, which is registered in the layer.xml file, just like any other action class that we want to be able to invoke from a toolbar button:
public class ListDeployedAppsAction extends CallableSystemAction {
private static JButton dropDownButton;
private static JPopupMenu popup;
static JMenu menu;
static void setProjectNames(File buildImplXML, String projectName, String projectType) {
Image icon = null;
if (projectType.equals("org.netbeans.modules.java.j2seproject")) {
icon = Utilities.loadImage("/org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png");
} else if (projectType.equals("org.netbeans.modules.web.project")) {
icon = Utilities.loadImage("/org/netbeans/modules/web/project/ui/resources/webProjectIcon.gif");
} else {
icon = Utilities.loadImage("/org/netbeans/modules/project/ui/resources/runProject.png");
}
ImageIcon image = new ImageIcon(icon);
menu = new JMenu();
JMenuItem subItemRun;
menu.setIcon(image);
menu.setText(projectName);
subItemRun = new JMenuItem("Run \\"" + projectName + "\\" again");
subItemRun.addActionListener(new RunActionListener(buildImplXML));
menu.add(subItemRun);
popup.add(menu);
}
static class RunActionListener implements ActionListener {
File file;
public RunActionListener(File file) {
this.file = file;
}
public void actionPerformed(ActionEvent e) {
try {
ActionUtils.runTarget(FileUtil.toFileObject(file), new String[]{"run"}, new Properties());
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} catch (IllegalArgumentException ex) {
Exceptions.printStackTrace(ex);
}
}
}
@Override
public Component getToolbarPresenter() {
Image iconImage = Utilities.loadImage("/org/netbeans/modules/project/ui/resources/runProject.png");
ImageIcon icon = new ImageIcon(iconImage);
popup = new JPopupMenu();
dropDownButton = DropDownButtonFactory.createDropDownButton(
new ImageIcon(
new BufferedImage(32, 32, BufferedImage.TYPE_BYTE_GRAY)),
popup);
dropDownButton.setIcon(icon);
return dropDownButton;
}
@Override
public void performAction() {
}
@Override
public String getName() {
return "Deployed Apps";
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
}
That's it. Next I need to add a submenu item for removing menu items. Maybe also a configuration dialog for tweaking the launch configurations that are registered in the list. By the way, in the process of moving my code from a window to a dropdown button factory, I was able to improve my code in several ways. Refactoring rocks. It really lets you look at your old code afresh.