Geertjan's Blog

  • February 4, 2011

org.netbeans.spi.project.SubprojectProvider (Part 3)

Geertjan Wielenga
Product Manager
Here Toni describes how NodeFactorySupport.createCompositeChildren is used to make a custom project type's logical view pluggable.

But what about a custom project type's lookup? For that, there is LookupProviderSupport.createCompositeLookup. When would you use this construction? When you want to make a custom project type's lookup pluggable.

For example, in the scenario discussed in this blog recently (part 1 and part 2), where you have a main project with a subproject. In those blog entries, the main project's module depended on the subproject's module, since that's where the SubprojectProvider was found. I.e., the "getLookup()" of the main project was like this:

public Lookup getLookup() {
if (lkp == null) {
lkp = Lookups.fixed(new Object[]{
new DemoSubprojectProvider(this), //Subproject of this project
return lkp;

But let's suppose we do not want to be tightly coupled like that. So, we're going to have an unknown number of subprojects contributed "from the outside", in which case the "getLookup()" of the main project needs to be defined like this, via LookupProviderSupport.createCompositeLookup:

public Lookup getLookup() {
if (lkp == null) {
lkp = LookupProviderSupport.createCompositeLookup(Lookups.fixed(
this, //project spec requires a project be in its own lookup
state, //allow outside code to mark the project as needing saving
new ActionProviderImpl(), //Provides standard actions like Build and Clean
new DemoDeleteOperation(),
new DemoCopyOperation(this),
new Info(), //Project information implementation
logicalView //Logical view of project implementation
), "Projects/org-demo-project/Lookup");
return lkp;

And then you can create separate modules for each subproject of the main project defined above. In each subproject's module, you'll have a SubprojectProvider, which needs to be annotated like this:

@ProjectServiceProvider(service=SubprojectProvider.class, projectType="org-demo-project")
public class DemoSubprojectProvider implements SubprojectProvider {

When you compile the module containing the above SubprojectProvider, you'll have this in your layer file:

<folder name="Projects">
<folder name="org-demo-project">
<folder name="Lookup">
<file name="org-demo-sub-project-type-DemoSubprojectProvider.instance">
methodvalue="org.netbeans.modules.projectapi.LazyLookupProviders.forProjectServiceProvider" name="instanceCreate"/>
<attr name="class" stringvalue="org.demo.sub.project.type.DemoSubprojectProvider"/>
<attr name="service" stringvalue="org.netbeans.spi.project.SubprojectProvider"/>

Note that the folder structure here is "Projects/org-demo-project/Lookup", which is what the "getLookup()" in the main project has set as the root folder where all extensions to its lookup are found.

And that's all. Now you have a custom project type with a pluggable lookup, as well as an example of something you'd plug into it, i.e., a SubprojectProvider. In this particular case, you could now have one module providing a main project, with X number of additional modules providing subprojects of the main project, without any of those modules needing a dependency on each other. However, if there is more than one implementation of SubprojectProvider, the LookupMerger class will become relevant, together with the @LookupMerger.Registration annotation, but that's for another time.

Thanks Jesse for help on this one.

Join the discussion

Comments ( 1 )
  • Timon Veenstra Tuesday, December 6, 2011

    I'm curious about possible DemoSubprojectProvider implementations.

    No additional information is provided to the DemoSubprojectProvider and it has no knowledge of the main project. So the list with sub projects cannot be relative to the main project. Should the provider maintain a static list with all created DemoSubProjects? Or perhaps scan some folder?

    I can think of some usages for this way of sub module injection, but I wonder if would have to put too much effort in keeping a valid set of sub modules to inject...

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.