NetBeans Project Type Module Tutorial
This tutorial demonstrates how to create a new project type in a NetBeans Platform application.
Before going further, make sure this is the tutorial you actually need!
- Rather than creating a new project type, you might want to extend an existing project type instead, as described in the NetBeans Project Type Extension Module Tutorial.
- For Maven-based NetBeans Platform applications, see How to Create a Custom Project Type in a Mavenized NetBeans Platform Application.
- If the projects for which you're creating a project type (whether on Ant or Maven based NetBeans Platform applications) need to use Ant as their build tool, you should use the NetBeans Ant-Based Project Type Module Tutorial instead.
Note: This document uses NetBeans Platform 7.2 and NetBeans IDE 7.2. If you are using an earlier version, see the previous version of this document.
Contents
- Introduction to Project Types
- Creating the Module Project
- Setting Dependencies
- Creating the Project Factory
- Creating the Project
- Registering the Project Type as Project Sample
To follow this tutorial, you need the software and resources listed in the following table.
Software or Resource | Version Required |
---|---|
NetBeans IDE | version 7.2 or above |
Java Developer Kit (JDK) | version 7 or above |
You will also make use of these icons, which you
can right-click here and download:
For troubleshooting purposes, you are welcome to download the completed tutorial source code.
Introduction to Project Types
A project type is a NetBeans Platform term for a grouping of folders and files that is treated as a single unit. Treating related folders and files as a single unit makes working with them easier for the end user. One way in which a project type simplifies life for the user is that you are able to fill the Projects window only with those folders and files that the end user is most likely to work. For example, the Java project type in NetBeans IDE helps the end user to work with the folders and files belonging to a single Java application.
Our project type will be defined by the existence of a file named "customer.txt". The tutorial assumes you have available, on disk, multiple folders containing such a file, for example as illustrated below:
As in the case of the folders named "customer1", "customer2", and "customer3" above, if a folder contains a file named "customer", with a "txt" extension, the NetBeans Platform will recognize the folder as a project. The user will be able to open the project into a NetBeans Platform application. The user will also be able to create new projects, via the New Projects window (Ctrl-Shift-N), which is where we will register some sample projects.
The following are the main NetBeans API classes we will be implementing in this tutorial:
Class | Description |
---|---|
org.netbeans.spi.project.ProjectFactory | Determines when a folder or file is a valid project and then creates the implemention of org.netbeans.api.project.Project. |
org.netbeans.api.project.Project | Represents the project. |
org.netbeans.spi.project.ui.LogicalViewProvider | Provides the logical view for the project. |
org.netbeans.api.project.ProjectInformation | Provides supplemental information for the project. |
org.netbeans.spi.project.ActionProvider | Provides one or more actions for the project. |
org.netbeans.spi.project.CopyOperationImplementation | Provides the Copy operation for the project. |
org.netbeans.spi.project.DeleteOperationImplementation | Provides the Delete operation for the project. |
Creating the Module Project
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.
- Choose File > New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules. Under Projects, select Module. Click Next.
- In the Name and Location panel, type CustomerProjectType in the Project Name field.
Change the Project Location to any directory on your computer.
Click Next. - In the Basic Module Configuration panel, type org.customer.project
in Code Name Base.
Click Finish.
The IDE creates the CustomerProjectType 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).
Setting Dependencies
We will need to make use of several NetBeans APIs. In this step, we select the modules that provide the NetBeans APIs that we will need.
- Right-click the project's Libraries node and choose "Add Module Dependency".
Select the following modules and click OK:
- Common Annotations
- Datasystems API
- Dialogs API
- File System API
- Lookup API
- Nodes API
- Project API
- Project UI API
- UI Utilities API
- Utilities API
- Expand the Libraries node and check that the following dependencies have been set in the previous step:
Creating the Project Factory
We start by implementing the org.netbeans.spi.project.ProjectFactory class.
Create a Java class named CustomerProjectFactory.
Change the default code to the following:
import java.io.IOException; import org.netbeans.api.project.Project; import org.netbeans.spi.project.ProjectFactory; import org.netbeans.spi.project.ProjectState; import org.openide.filesystems.FileObject; import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service=ProjectFactory.class) public class CustomerProjectFactory implements ProjectFactory { public static final String PROJECT_FILE = "customer.txt"; //Specifies when a project is a project, i.e., //if "customer.txt" is present in a folder: @Override public boolean isProject(FileObject projectDirectory) { return projectDirectory.getFileObject(PROJECT_FILE) != null; } //Specifies when the project will be opened, i.e., if the project exists: @Override public Project loadProject(FileObject dir, ProjectState state) throws IOException { return isProject(dir) ? new CustomerProject(dir, state) : null; } @Override public void saveProject(final Project project) throws IOException, ClassCastException { // leave unimplemented for the moment } }
Note: The @ServiceProvider annotation used in the class signature above will cause a META-INF/services file to be created when the module is compiled. Within that folder, a file named after the fully qualified name of the interface will be found, containing the fully qualified name of the implementing class. That is the standard JDK mechanism, since JDK 6, for registering implementations of interfaces. That is how project types are registered in the NetBeans Plaform.
Instead of ProjectFactory, consider implementing the newer ProjectFactory2. ProjectFactory2 is a performance correction to ProjectFactory, done in a compatible way. If you implement ProjectFactory2, the project will not need to be loaded, which can take some time, especially in populating the Lookup, and the project icon appears fast in the Open Project dialog. If you implement only ProjectFactory, more memory is consumed and projects are loaded even if not used or opened in the end. The main effective place to see the difference visually is when you have many projects in a single folder. The pattern itself is fairly common in the Eclipse world, for example. Interfaces are extended as InterfaceExt, InterfaceExt2, InterfaceExt3, etc. The general idea is that typically you should always implement the last extension to the base interface. But the core codebase dealing with the interfaces can handle all of the variants.
Creating the Project
Next, we implement the org.netbeans.api.project.Project class.
Create a Java class named CustomerProject.
We'll start with a simple skeleton implementation:
import org.netbeans.api.project.Project; import org.netbeans.spi.project.ProjectState; import org.openide.filesystems.FileObject; import org.openide.util.Lookup; public class CustomerProject implements Project { CustomerProject(FileObject dir, ProjectState state) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public FileObject getProjectDirectory() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Lookup getLookup() { throw new UnsupportedOperationException("Not supported yet."); } }
The getLookup method, in the code above, is the key to the NetBeans project infrastructure. When you create new features for a project type, such as its logical view, its popup actions, or its customizer, you register them in the project via its getLookup method.
- Let's set up our project class so that we can start
using it to register the project's features. Fill out
the class by setting fields and add code to the getLookup
method to prepare it for the following sections.
import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.ImageIcon; import org.netbeans.api.annotations.common.StaticResource; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectInformation; import org.netbeans.spi.project.ProjectState; import org.openide.filesystems.FileObject; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.lookup.Lookups; public class CustomerProject implements Project { private final FileObject projectDir; private final ProjectState state; private Lookup lkp; CustomerProject(FileObject dir, ProjectState state) { this.projectDir = dir; this.state = state; } @Override public FileObject getProjectDirectory() { return projectDir; } @Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ // register your features here }); } return lkp; } }
- Now let's work on the features that we'd like
our project to have. In each case, we define
the feature and then we register the feature
in the project's Lookup.
- Creating and Registering the Project Information
- Creating and Registering the Project Logical View
- Creating and Registering the Project Node Children
- Creating and Registering the Project Customizer
- Creating and Registering the Project Subprojects
Creating and Registering the Project Information
In this section, you register minimum NetBeans project support, that is, you create and register a class that provides an icon and a display name for the project.
- Put the icon.png file, referred to at the start of this tutorial, into the org.customer.project package.
- As an inner class of the CustomerProject class,
define the project information as follows:
private final class Info implements ProjectInformation { @StaticResource() public static final String CUSTOMER_ICON = "org/customer/project/icon.png"; @Override public Icon getIcon() { return new ImageIcon(ImageUtilities.loadImage(CUSTOMER_ICON)); } @Override public String getName() { return getProjectDirectory().getName(); } @Override public String getDisplayName() { return getName(); } @Override public void addPropertyChangeListener(PropertyChangeListener pcl) { //do nothing, won't change } @Override public void removePropertyChangeListener(PropertyChangeListener pcl) { //do nothing, won't change } @Override public Project getProject() { return CustomerProject.this; } }
-
Now register the ProjectInformation in the Lookup of the project as follows:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ new Info(), }); } return lkp; }
- Run the module. Your application
starts up and your module is installed into it.
Go to File | Open Project and, when you browse
to folders containing a "customer.txt" file, notice
that the folders are recognized as projects and
show the icon you defined in the ProjectInformation
class above:
When you open a project, notice that all the folders and files in the project are shown in the Projects window and that, when you right-click on the project, several default popup actions are shown:
Now that you can open folders as projects into your application, let's work on the project's logical view. The logical view is displayed in the Projects window. The Projects window typically only shows the most important files or folders that the user should work with, together with the related display names, icons, and popup actions.
Creating and Registering the Project Logical View
In this section, you define the logical view of your project, as shown in the Projects window of your application.
- As an inner class of the CustomerProject class,
define the project logical view as follows:
class CustomerProjectLogicalView implements LogicalViewProvider { @StaticResource() public static final String CUSTOMER_ICON = "org/customer/project/icon.png"; private final CustomerProject project; public CustomerProjectLogicalView(CustomerProject project) { this.project = project; } @Override public Node createLogicalView() { try { //Obtain the project directory's node: FileObject projectDirectory = project.getProjectDirectory(); DataFolder projectFolder = DataFolder.findFolder(projectDirectory); Node nodeOfProjectFolder = projectFolder.getNodeDelegate(); //Decorate the project directory's node: return new ProjectNode(nodeOfProjectFolder, project); } catch (DataObjectNotFoundException donfe) { Exceptions.printStackTrace(donfe); //Fallback-the directory couldn't be created - //read-only filesystem or something evil happened return new AbstractNode(Children.LEAF); } } private final class ProjectNode extends FilterNode { final CustomerProject project; public ProjectNode(Node node, CustomerProject project) throws DataObjectNotFoundException { super(node, new FilterNode.Children(node), new ProxyLookup( new Lookup[]{ Lookups.singleton(project), node.getLookup() })); this.project = project; } @Override public Action[] getActions(boolean arg0) { return new Action[]{ CommonProjectActions.newFileAction(), CommonProjectActions.copyProjectAction(), CommonProjectActions.deleteProjectAction(), CommonProjectActions.closeProjectAction() }; } @Override public Image getIcon(int type) { return ImageUtilities.loadImage(CUSTOMER_ICON); } @Override public Image getOpenedIcon(int type) { return getIcon(type); } @Override public String getDisplayName() { return project.getProjectDirectory().getName(); } } @Override public Node findPath(Node root, Object target) { //leave unimplemented for now return null; } }
Many project actions are available for you to use, as you can see from the code completion:
- As before, register the feature in the Lookup of the project:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ new Info(), new CustomerProjectLogicalView(this), }); } return lkp; }
- Run the module again and open a customer project again. You should see the following:
The project node now shows the display name, icon, and popup actions that you defined.
Creating and Registering the Project Node Children
In this section, you learn how to define which folders and files should be displayed in the logical view, that is, the Projects window. Currently, you are showing all folders and files because the children of the project node are defined by FilterNode.Children(node), which means "display all the children of the node".
- Change the constructor of the ProjectNode as follows:
public ProjectNode(Node node, CustomerProject project) throws DataObjectNotFoundException { super(node, NodeFactorySupport.createCompositeChildren( project, "Projects/org-customer-project/Nodes"), // new FilterNode.Children(node), new ProxyLookup( new Lookup[]{ Lookups.singleton(project), node.getLookup() })); this.project = project; }
- Register the project in its own Lookup:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ this, new Info(), new CustomerProjectLogicalView(this),}); } return lkp; }
- Create a new Java class TextsNodeFactory in a new package org.customer.project.nodes
as follows, while taking special note of the @NodeFactory.Registration annotation:
package org.customer.project.nodes; import java.util.ArrayList; import java.util.List; import javax.swing.event.ChangeListener; import org.customer.project.CustomerProject; import org.netbeans.api.project.Project; import org.netbeans.spi.project.ui.support.NodeFactory; import org.netbeans.spi.project.ui.support.NodeList; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.nodes.FilterNode; import org.openide.nodes.Node; import org.openide.util.Exceptions; @NodeFactory.Registration(projectType = "org-customer-project", position = 10) public class TextsNodeFactory implements NodeFactory { @Override public NodeList<?> createNodes(Project project) { CustomerProject p = project.getLookup().lookup(CustomerProject.class); assert p != null; return new TextsNodeList(p); } private class TextsNodeList implements NodeList<Node> { CustomerProject project; public TextsNodeList(CustomerProject project) { this.project = project; } @Override public List<Node> keys() { FileObject textsFolder = project.getProjectDirectory().getFileObject("texts"); List<Node> result = new ArrayList<Node>(); if (textsFolder != null) { for (FileObject textsFolderFile : textsFolder.getChildren()) { try { result.add(DataObject.find(textsFolderFile).getNodeDelegate()); } catch (DataObjectNotFoundException ex) { Exceptions.printStackTrace(ex); } } } return result; } @Override public Node node(Node node) { return new FilterNode(node); } @Override public void addNotify() { } @Override public void removeNotify() { } @Override public void addChangeListener(ChangeListener cl) { } @Override public void removeChangeListener(ChangeListener cl) { } } }
- Run the module again and open a customer project again. Make sure the
project has a subfolder named "texts", with some content. You should see the following, that
is, the content of the "texts" folder is shown in the Projects window, which
exists to provide a logical view, while the Files
window shows the complete folder structure:
An important point to realize in this section is that the @NodeFactory.Registration annotation can be used to register new child nodes of the customer project node, either within the current module or via external modules. In this way, the logical view of your project is extensible, that is, logical views can be pluggable, if an extension point is created as part of its definition, as shown in step 1 of this section.
Creating and Registering the Project Customizer
In this section, you learn how to create a pluggable customizer. When the user right-clicks the project node, they will see a Properties menu item. When they click it, the customizer will open. The categories in the customizer can be contributed by external modules, that is, the customizer will be created to be extensible.
- Register the customizer action in the logical view of the project, as follows:
@Override public Action[] getActions(boolean arg0) { return new Action[]{ CommonProjectActions.newFileAction(), CommonProjectActions.copyProjectAction(), CommonProjectActions.deleteProjectAction(), CommonProjectActions.customizeProjectAction(), CommonProjectActions.closeProjectAction() }; }
- Run the module and right-click the project node. You should see that the
Properties popup menu item is present, but disabled:
- Register a skeleton customizer in the Lookup of the project:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ this, new Info(), new CustomerProjectLogicalView(this), new CustomizerProvider() { @Override public void showCustomizer() { JOptionPane.showMessageDialog( null, "customizer for " + getProjectDirectory().getName()); } }, }); } return lkp; }
- Run the module again and right-click the project node. You should see that the
Properties popup menu item is now enabled:
Click the menu item and you should see your JOptionPane:
- Now we create the infrastructure for our
pluggable Project Properties window:
package org.customer.project; import java.awt.Dialog; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import org.netbeans.api.project.ProjectUtils; import org.netbeans.spi.project.ui.CustomizerProvider; import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.awt.StatusDisplayer; import org.openide.util.lookup.Lookups; public class CustomerCustomizerProvider implements CustomizerProvider { public final CustomerProject project; public static final String CUSTOMIZER_FOLDER_PATH = "Projects/org-customer-project/Customizer"; public CustomerCustomizerProvider(CustomerProject project) { this.project = project; } @Override public void showCustomizer() { Dialog dialog = ProjectCustomizer.createCustomizerDialog( //Path to layer folder: CUSTOMIZER_FOLDER_PATH, //Lookup, which must contain, at least, the Project: Lookups.fixed(project), //Preselected category: "", //OK button listener: new OKOptionListener(), //HelpCtx for Help button of dialog: null); dialog.setTitle(ProjectUtils.getInformation(project).getDisplayName()); dialog.setVisible(true); } private class OKOptionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { StatusDisplayer.getDefault().setStatusText("OK button clicked for " + project.getProjectDirectory().getName() + " customizer!"); } } }
- Next, rewrite the project's Lookup so that
the above class is created within it:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ this, new Info(), new CustomerProjectLogicalView(this), new CustomerCustomizerProvider(this) }); } return lkp; }
- In a new package org.customer.project.panels,
create a new Java class named GeneralCustomerProperties,
with this content:
package org.customer.project.panels; import javax.swing.JComponent; 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 GeneralCustomerProperties implements ProjectCustomizer.CompositeCategoryProvider { private static final String GENERAL = "General"; @ProjectCustomizer.CompositeCategoryProvider.Registration( projectType = "org-customer-project", position = 10) public static GeneralCustomerProperties createGeneral() { return new GeneralCustomerProperties(); } @NbBundle.Messages("LBL_Config_General=General") @Override public Category createCategory(Lookup lkp) { return ProjectCustomizer.Category.create( GENERAL, Bundle.LBL_Config_General(), null); } @Override public JComponent createComponent(Category category, Lookup lkp) { return new JPanel(); } }
Note the usage of the @ProjectCustomizer.CompositeCategoryProvider.Registration annotation above. Using that annotation, you can register new panels in the Project Properties dialog, via the extension point you created in step 5 above. In this way, each panel can be contributed by external modules. For another example, see Adding New Tabs to the Project Properties Dialog in NetBeans IDE.
- Run the module again and right-click the project node. When you click the
Properties menu item, you should see the Project Properties dialog, with
one category:
When you click the OK button, you will see a message in the status bar. The message is provided by the OKOptionListener you defined above.
You now have the start of a project customizer.
Creating and Registering the Project Subprojects
In this section, you learn how to create new project types that are nested within other project types:
Above, you can see that the "customer3" project has several folders. One of those folders is named "reports", containing subfolders, each of which contains a file named "report.xml". In the instructions that follow, you will create a new project type for folders containing a file named "report.xml", while also being shown how to register those projects as subprojects of the customer project.
- Following the instructions at the start of this tutorial, create a new ProjectFactory that recognizes folders containing a file named "report.xml" as a project of type ReportsSubProject. Define a ProjectInformation and a ProjectLogicalView for your ReportsSubProject.
- Create a SubprojectProvider that looks inside the customer project's "reports"
folder for projects of your type:
public class ReportsSubprojectProvider implements SubprojectProvider { private final CustomerProject project; public ReportsSubprojectProvider(CustomerProject project) { this.project = project; } @Override public Set<? extends Project> getSubprojects() { return loadProjects(project.getProjectDirectory()); } private Set loadProjects(FileObject dir) { Set newProjects = new HashSet(); FileObject reportsFolder = dir.getFileObject("reports"); if (reportsFolder != null) { for (FileObject childFolder : reportsFolder.getChildren()) { try { Project subp = ProjectManager.getDefault(). findProject(childFolder); if (subp != null && subp instanceof ReportsSubProject) { newProjects.add((ReportsSubProject) subp); } } catch (IOException ex) { Exceptions.printStackTrace(ex); } catch (IllegalArgumentException ex) { Exceptions.printStackTrace(ex); } } } return Collections.unmodifiableSet(newProjects); } @Override public void addChangeListener(ChangeListener cl) { } @Override public void removeChangeListener(ChangeListener cl) { } }
- Register the SubprojectProvider in the customer project's Lookup:
@Override public Lookup getLookup() { if (lkp == null) { lkp = Lookups.fixed(new Object[]{ this, new Info(), new CustomerProjectLogicalView(this), new CustomerCustomizerProvider(this), new ReportsSubprojectProvider(this) }); } return lkp; }
- Similar to the TextsNodeFactory that you created earlier in this tutorial,
create a new Java class ReportsSubProjectNodeFactory as follows,
while again taking special note of the @NodeFactory.Registration annotation, which
registers the NodeFactory into the logical view of the customer project:
@NodeFactory.Registration(projectType = "org-customer-project", position = 20) public class ReportsSubProjectNodeFactory implements NodeFactory { @StaticResource() public static final String SUB_ICON = "org/customer/project/sub/icon.png"; @Override public NodeList<?> createNodes(Project project) { ReportsSubprojectProvider rsp = project.getLookup(). lookup(ReportsSubprojectProvider.class); assert rsp != null; return new ReportsNodeList(rsp.getSubprojects()); } private class ReportsNodeList implements NodeList<Project> { Set<? extends Project> subprojects; public ReportsNodeList(Set<? extends Project> subprojects) { this.subprojects = subprojects; } @Override public List<Project> keys() { List<Project> result = new ArrayList<Project>(); for (Project oneReportSubProject : subprojects) { result.add(oneReportSubProject); } return result; } @Override public Node node(Project node) { FilterNode fn = null; try { fn = new FilterNode(DataObject.find(node. getProjectDirectory()).getNodeDelegate()){ @Override public Image getIcon(int type) { return ImageUtilities.loadImage(SUB_ICON); } @Override public Image getOpenedIcon(int type) { return ImageUtilities.loadImage(SUB_ICON); } }; } catch (DataObjectNotFoundException ex) { Exceptions.printStackTrace(ex); } return fn; } @Override public void addNotify() { } @Override public void removeNotify() { } @Override public void addChangeListener(ChangeListener cl) { } @Override public void removeChangeListener(ChangeListener cl) { } } }
Above, reference is made to an icon. Use your own, 16x16 pixels in size, or use one of the two shown at the top of this tutorial.
- Run the module again, go to the Open Project dialog,
and notice that subprojects are recognized
and that you can open them:
Also, when you've selected a customer project in the Open Project dialog, the Open Project dialog lets you open the subprojects, too:
Using the instructions in this subsection, you can create a richly structured and deeply nested project hierarchy, because each subproject can provide its own subprojects, too. For further information on this topic, see this blog entry, this blog entry, and this blog entry.
In this section, you have defined the basic infrastructure of a new type of project in your NetBeans Platform application.
Registering the Project Type as Project Sample
In this section, we create some project samples that make use of our project type. We also register these project samples in the New Project window of our application.
Run the module that you created in this tutorial. A new instance of your NetBeans Platform application starts up, with your project type installed via your module. If you're creating the project type for NetBeans IDE, continue to the next step.
If you're creating the project type for some other application on the NetBeans Platform, you will need to include the apisupport modules from NetBeans IDE in your application, temporarily, to complete the steps that follow.
Open the sample projects you created in the previous step, which you're now able to do since you have installed a module providing your project type.
-
Also open the module itself. Create a new subpackage, named "samples", as shown below. Then right-click the package and choose New | Other | Module Development, and select Project Template as shown below:
Use the New Project Template wizard to register your first sample project:
Click Next. Specify the name of the template, the display text, and the package where the template should be registered:
Once you have completed the wizard, use it again to register other customer projects as samples.
Check that the module you're developing now looks something like this in the Projects window:
You have now used the New Project Template wizard to register some project samples in your application. Also notice that you have some ZIP files containing your sample projects, created by the Project Template wizard, together with several classes from the NetBeans Wizard API. For further information, refer to the NetBeans Project Sample Module Tutorial.
- After closing the second instance of the IDE with the installed module, close and reopen the module in the original IDE before trying to run it. The reason for this is that the nbproject\private\platform-private.properties is changed by the second instance of the IDE to point to the testuserdir of the module, when the module is opened. Reopening the module fixes this problem.
- Run your module again and go to File | New Project. You should see your new project samples,
together with any other project samples registered in the application:
Complete the wizard. At the end of the wizard, the ZIP file is unzipped and the new project is created.
You now have support for a new type of project, including a set of samples that your users can use to create skeleton projects of your type.
Next Steps
For more information about creating and developing NetBeans modules, see the following resources: