blob: ce8ed6f942994906b233d17c57ab44675dbe14e2 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- -*- xhtml -*- -->
<title>NetBeans Project Type Extension Module Tutorial for NetBeans Platform 7.2</title>
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/>
<meta name="AUDIENCE" content="NBUSER"/>
<meta name="TYPE" content="ARTICLE"/>
<meta name="EXPIRES" content="N"/>
<meta name="developer" content="gwielenga@netbeans.org"/>
<meta name="indexed" content="y"/>
<meta name="description"
content="A short guide to using the APIs for extending project types."/>
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans Project Type Extension Module Tutorial</h1>
<p>This tutorial demonstrates how to extend an existing project type.</p>
<p><strong class="notes">Note: </strong>This document uses NetBeans Platform 7.2 and
NetBeans IDE 7.2. If you
are using an earlier version, see <a href="71/nbm-projectextension.html">the previous version
of this document</a>.</p>
<p><b>Contents</b></p>
<p><img src="../../images/articles/72/netbeans-stamp.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.2" title="Content on this page applies to NetBeans IDE 7.2"/></p>
<ul class="toc">
<li><a href="#intro">Introduction to Project Extensions</a></li>
<li><a href="#creatingthemoduleproject">Creating the Module Project</a></li>
<li><a href="#scenarios">Project Extension Scenarios</a>
<ul>
<li><a href="#extendingthelookup">Scenario 1: Extending the Project Lookup</a></li>
<li><a href="#extendingthelogicalview">Scenario 2: Extending the Project Logical View</a></li>
<li><a href="#extendingthecustomizer">Scenario 3: Extending the Project Customizer</a></li>
</ul>
</li>
</ul>
<p><b>To follow this tutorial, you need the software and resources listed in the following
table.</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">Software or Resource</th>
<th class="tblheader" scope="col">Version Required</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">version 7.2 or above</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td>
<td class="tbltd1">version 6 or above</td>
</tr>
</tbody>
</table>
<p>You will also make use of this icon, which you
can right-click here and download: <img alt="" src="../../images/tutorials/projectextensions/webPagesBadge.gif" /></p>
<h2 class="tutorial"><a name="intro"></a>Introduction to Project Extensions</h2>
<p>New NetBeans IDE APIs since NetBeans IDE 6.0
enable you to add new nodes to an existing project type's logical view, new objects to an
existing project type's lookup, and new panels to an existing project type's Project
Properties dialog box. For example, in this tutorial,
to illustrate these extensions, we extend the web application project type's
logical view, by adding a new "Important Files" node, exposing the content of the
project's "nbproject" folder, as shown here:</p>
<p><img src="../../images/tutorials/cc/72/result-2.png" alt="New node"/></p>
<p>Prior to NetBeans IDE 6.0, no NetBeans IDE APIs existed for extending existing project types.
Instead, you would need to create new project types from scratch. From 6.0 onwards, you are
recommended to extend existing project types rather than create new ones, where possible. This will
keep the number of project types to a minimum and avoid a large number of project
types with very small differences. However, it is, of course, always possible to create project
types from scratch, as before, following the <a href="https://platform.netbeans.org/tutorials/nbm-projecttype.html">NetBeans Project Type Module Tutorial</a>.</p>
<p>Annotations are used throughout this tutorial to register the lookup extension,
logical view extension, and project customizer extension. As you will see
below, the extensions will be registered for the project type
"org-netbeans-modules-web-project", which is the web application project type.
Here is a list of strings representing other project types supported by
NetBeans IDE:</p>
<ul>
<li>org-netbeans-modules-ant-freeform</li>
<li>org-netbeans-modules-apisupport-project</li>
<li>org-netbeans-modules-apisupport-project-suite</li>
<li>org-netbeans-modules-j2ee-archiveproject</li>
<li>org-netbeans-modules-j2ee-clientproject</li>
<li>org-netbeans-modules-j2ee-earproject</li>
<li>org-netbeans-modules-j2ee-ejbjarproject</li>
<li>org-netbeans-modules-java-j2seproject</li>
</ul>
<p>More project types may be available, depending on the modules that are part
of your specific installation of NetBeans IDE or other application on the
NetBeans Platform.</p>
<h2 class="tutorial"><a name="creatingthemoduleproject"></a>Creating the Module Project</h2>
<p>We begin by working through the New Module Project
wizard. At the end of it, we will have a basic
source structure, with some default files, that
every NetBeans module requires.</p>
<div class="indent">
<ol>
<li>Choose File &gt; New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules.
Under Projects, select Module. Click Next.</li>
<li>In the Name and Location panel, type <tt>ImportantWebFiles</tt> in the Project Name field.
Change the Project Location to any directory on your computer. Click Next.</li>
<li>In the Basic Module Configuration panel, type <tt>org.netbeans.modules.importantwebfiles</tt>
in Code Name Base. Click Finish. The IDE creates the <tt>ImportantWebFiles</tt>
project. The project contains all of your sources and
project metadata, such as the project's Ant build script. The project
opens in the IDE. You can view its logical structure in the Projects window (Ctrl-1) and its
file structure in the Files window (Ctrl-2).
</li>
<li>Right-click the project's Libraries node, choose Add Module Dependency, and then set dependencies
on the following modules:
<br/>
<br/>
<ul>
<li>Datasystems API</li>
<li>File System API</li>
<li>Lookup API</li>
<li>Nodes API</li>
<li>Project API</li>
<li>Project UI API</li>
<li>Utilities API</li>
</ul>
<br/>
<p>You should now see the following dependencies have been set:</p>
<br/>
<p><img src="../../images/tutorials/cc/72/deps-1.png" alt="New node"/></p>
</li>
</ol>
</div>
<p>Your module structure is ready, the dependencies have been set, and you can now begin coding.</p>
<!-- ===================================================================================== -->
<h2><a name="scenarios"></a>Project Extension Scenarios</h2>
<p>Three separate, independent scenarios are described below. Depending on your needs,
extend the project of your choice in one or more of the following ways:</p>
<ul>
<li><a href="#extendingthelookup">Scenario 1: Extending the Project Lookup</a></li>
<li><a href="#extendingthelogicalview">Scenario 2: Extending the Project Logical View</a></li>
<li><a href="#extendingthecustomizer">Scenario 3: Extending the Project Customizer</a></li>
</ul>
<div class="indent">
<h3 class="tutorial"><a name="extendingthelookup"></a>Scenario 1: Extending the Project Lookup</h3>
<p>In this section, we register a class named <tt>ServiceImpl</tt> into the
<tt>Lookup</tt> of web projects. We create an <tt>Action</tt> to verify
that the object has been registered successfully.</p>
<div class="indent">
<ol>
<li><p>Create a Java class named <tt>Service</tt>. Change
the default code to the following:</p>
<pre class="examplecode">import javax.swing.JOptionPane;
public abstract class Service {
static {
JOptionPane.showMessageDialog(null, "===> loading Service");
}
public abstract String m();
}</pre> </li>
<li><p>Create a new Java class named <tt>ServiceImpl</tt>. Change
the default code to the following:</p>
<pre class="examplecode">import javax.swing.JOptionPane;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.spi.project.ProjectServiceProvider;
<a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectapi/org/netbeans/spi/project/ProjectServiceProvider.html">@ProjectServiceProvider</a>(
service=Service.class,
projectType="org-netbeans-modules-web-project")
public class ServiceImpl extends Service {
static {
JOptionPane.showMessageDialog(null, "===> loading ServiceImpl");
}
private final Project p;
public ServiceImpl(Project p) {
this.p = p;
JOptionPane.showMessageDialog(null, "===> new ServiceImpl on " + p);
}
@Override
public String m() {
return ProjectUtils.getInformation(p).getDisplayName();
}
}</pre> </li>
<li><p>Create a new Java class named <tt>TestAction</tt>. Change
the default code to the following:</p>
<pre class="examplecode">import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ui.OpenProjects;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;
@ActionID(
category = "File",
id = "org.netbeans.modules.importantwebfiles.TestAction")
@ActionRegistration(
displayName = "#CTL_TestAction")
@ActionReference(
path = "Menu/File",
position = 0)
@Messages("CTL_TestAction=Test")
public final class TestAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "===> running action");
for (Project p : OpenProjects.getDefault().getOpenProjects()) {
Service s = p.getLookup().lookup(Service.class);
if (s != null) {
JOptionPane.showMessageDialog(null, "===> got a service: " + s.m());
} else {
JOptionPane.showMessageDialog(null, "===> nothing for " + p);
}
}
}
}</pre> </li>
</ol>
</div>
<p>Run the module to install it into a new instance of NetBeans IDE.
Open a few NetBeans projects. Invoke the <tt>Action</tt>
and observe the <tt>JOptionPanes</tt> to see the result. Depending on
whether a project is a web project, you will get different messages.</p>
</div>
<!-- ===================================================================================== -->
<div class="indent">
<h3 class="tutorial"><a name="extendingthelogicalview"></a>Scenario 2: Extending the Project Logical View</h3>
<p>In this section, we change the node hierarchy in the Projects window for an existing project type.
We start by implementing the <tt><a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/support/NodeFactory.html">NodeFactory</a></tt>
class, which we will register via an annotation.</p>
<div class="indent">
<ol>
<li><p>Create a Java class called <tt>ImportantFilesNodeFactory</tt>. Change
the default code to the following:</p>
<pre class="examplecode">import org.netbeans.api.project.Project;
import org.netbeans.spi.project.ui.support.NodeFactory;
import org.netbeans.spi.project.ui.support.NodeFactorySupport;
import org.netbeans.spi.project.ui.support.NodeList;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.Exceptions;
<a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/support/NodeFactory.Registration.html">@NodeFactory.Registration</a>(projectType = "org-netbeans-modules-web-project")
public class ImportantFilesNodeFactory implements <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/support/NodeFactory.html">NodeFactory</a> {
@Override
public NodeList createNodes(Project project) {
//Optionally, only return a new node
//if some item is in the project's lookup:
//MyCoolLookupItem item = project.getLookup().lookup(MyCoolLookupItem.class);
//if (item != null) {
try {
ImportantFilesNode nd = new ImportantFilesNode(project);
return NodeFactorySupport.fixedNodeList(nd);
} catch (DataObjectNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
//}
//If the above try/catch fails, e.g.,
//our item isn't in the lookup,
//then return an empty list of nodes:
return NodeFactorySupport.fixedNodeList();
}
}</pre></li>
<li><p>Create a new Java class called <tt>ImportantFilesNode</tt>, which will filter
the node of the project's "nbproject" folder. A new display name
and icon will be defined for that folder. Therefore, change
the default code to the following:</p>
<pre class="examplecode">import java.awt.Image;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.project.Project;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.FilterNode;
import org.openide.util.ImageUtilities;
public class ImportantFilesNode extends <a href="http://bits.netbeans.org/dev/javadoc/org-openide-nodes/org/openide/nodes/FilterNode.html">FilterNode</a> {
@StaticResource
private static final String IMAGE = "org/netbeans/modules/"
+ "importantwebfiles/webPagesBadge.gif";
public ImportantFilesNode(Project proj) throws DataObjectNotFoundException {
super(DataObject.find(proj.getProjectDirectory().
getFileObject("nbproject")).getNodeDelegate());
}
@Override
public String getDisplayName() {
return "Important Files";
}
//Next, we add icons, for the default state, which is
//closed, and the opened state; we will make them the same.
//
//Icons in project logical views are
//based on combinations--you can combine the node's own icon
//with a distinguishing badge that is merged with it. Here we
//first obtain the icon from a data folder, then we add our
//badge to it by merging it via a NetBeans API utility method:
@Override
public Image getIcon(int type) {
DataFolder root = DataFolder.findFolder(FileUtil.getConfigRoot());
Image original = root.getNodeDelegate().getIcon(type);
return ImageUtilities.mergeImages(original,
ImageUtilities.loadImage(IMAGE), 7, 7);
}
@Override
public Image getOpenedIcon(int type) {
DataFolder root = DataFolder.findFolder(FileUtil.getConfigRoot());
Image original = root.getNodeDelegate().getIcon(type);
return ImageUtilities.mergeImages(original,
ImageUtilities.loadImage(IMAGE), 7, 7);
}
}</pre> </li>
<li>Right-click this icon and save it in the main package of your module: <img alt="" src="../../images/tutorials/projectextensions/webPagesBadge.gif" /></li>
</ol>
</div>
<p>Run the module and you will notice that web applications have your newly defined node,
exposing the project's "nbproject" folder:
<br/></p>
<p><img src="../../images/tutorials/cc/72/result-2.png" alt="New node"/></p>
</div>
<!-- ======================================================================================= -->
<div class="indent">
<h3 class="tutorial"><a name="extendingthecustomizer"></a>Scenario 3: Extending the Project Customizer</h3>
<p>In this section, we create two new tabs in the Project Properties dialog of the web application project type.</p>
<div class="indent">
<ol>
<li><p>Create a Java class called <tt>ImportantFilesCustomizerTab</tt>. Change
the default code to the following:</p>
<pre class="examplecode">import java.awt.BorderLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.netbeans.spi.project.ui.support.ProjectCustomizer;
import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
public class ImportantFilesCustomizerTab
implements <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/support/ProjectCustomizer.CompositeCategoryProvider.html">ProjectCustomizer.CompositeCategoryProvider</a> {
private final String name;
private ImportantFilesCustomizerTab(String name) {
this.name = name;
}
@Override
public Category createCategory(Lookup lkp) {
return ProjectCustomizer.Category.create(name, name, null);
}
@Override
public JComponent createComponent(Category category, Lookup lkp) {
JPanel jPanel1 = new JPanel();
jPanel1.setLayout(new BorderLayout());
jPanel1.add(new JLabel(name), BorderLayout.CENTER);
return jPanel1;
}
@NbBundle.Messages({"LBL_Config=Configuration"})
<a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-projectuiapi/org/netbeans/spi/project/ui/support/ProjectCustomizer.CompositeCategoryProvider.Registration.html">@ProjectCustomizer.CompositeCategoryProvider.Registration</a>(
projectType = "org-netbeans-modules-web-project",
position = 10)
public static ImportantFilesCustomizerTab createMyDemoConfigurationTab() {
return new ImportantFilesCustomizerTab(Bundle.LBL_Config());
}
}</pre> </li>
<li><p>Run the module. Right-click a web application's project node and choose Properties. Notice
the new tab that has been added. The <tt>createCategory</tt> method above defines
the left side of the screenshot below, while the right side is defined by the <tt>createComponent</tt> method.</p>
<br/>
<p><img src="../../images/tutorials/cc/72/result-3.png" alt="New node"/></p>
</li>
<li><p>Now we'll change the class so that two tabs are created, instead of one:</p>
<pre class="examplecode">import java.awt.BorderLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.netbeans.spi.project.ui.support.ProjectCustomizer;
import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
public class ImportantFilesCustomizerTab
implements ProjectCustomizer.CompositeCategoryProvider {
private final String name;
private ImportantFilesCustomizerTab(String name) {
this.name = name;
}
@Override
public Category createCategory(Lookup lkp) {
ProjectCustomizer.Category toReturn = null;
if (Bundle.LBL_Config1().equals(name)) {
toReturn = ProjectCustomizer.Category.create(
Bundle.LBL_Config1(),
Bundle.LBL_Config1(),
null);
} else {
toReturn = ProjectCustomizer.Category.create(
Bundle.LBL_Config2(),
Bundle.LBL_Config2(),
null);
}
return toReturn;
}
@Override
public JComponent createComponent(Category category, Lookup lkp) {
String nm = category.getName();
if (name.equals(nm)) {
JPanel jPanel1 = new JPanel();
jPanel1.setLayout(new BorderLayout());
jPanel1.add(new JLabel(name), BorderLayout.CENTER);
return jPanel1;
} else {
JPanel jPanel2 = new JPanel();
jPanel2.setLayout(new BorderLayout());
jPanel2.add(new JLabel(name), BorderLayout.CENTER);
return jPanel2;
}
}
@NbBundle.Messages({"LBL_Config1=ConfigurationPart1"})
@ProjectCustomizer.CompositeCategoryProvider.Registration(
projectType = "org-netbeans-modules-web-project",
position = 10)
public static ImportantFilesCustomizerTab createMyDemoConfigurationTab1() {
return new ImportantFilesCustomizerTab(Bundle.LBL_Config1());
}
@NbBundle.Messages({"LBL_Config2=ConfigurationPart2"})
@ProjectCustomizer.CompositeCategoryProvider.Registration(
projectType = "org-netbeans-modules-web-project",
position = 20)
public static ImportantFilesCustomizerTab createMyDemoConfigurationTab2() {
return new ImportantFilesCustomizerTab(Bundle.LBL_Config2());
}
}</pre> </li>
</ol>
</div>
<p>Run the module again and notice that you now have two new tabs:</p>
<p><img src="../../images/tutorials/cc/72/result-4.png" alt="New node"/></p>
</div>
<p>In this tutorial, you have learned how to extend the project's lookup, logical view, and customizer.</p>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback:%20Project%20Extension%207.2%20Module%20Tutorial">Send Us Your Feedback</a></div>
<!-- ======================================================================================== -->
<h2><a name="nextsteps"></a>Next Steps</h2>
<p>For more information about creating and developing NetBeans modules, see the following resources: </p>
<ul>
<li><a href="https://netbeans.org/kb/trails/platform.html">Other Related Tutorials</a></li>
<li><a href="https://netbeans.org/download/dev/javadoc/">NetBeans API Javadoc</a></li>
</ul>
</body>
</html>