| <!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 the NetBeans Platform</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/81/netbeans-stamp.png" 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 7 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 > 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&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> |