Wednesday October 07, 2009
NetBeans Common Server API
There's a new set of NetBeans APIs/SPIs under development, called Common Server. How does it compare to the Java EE Server APIs? In the words of its creator, Petr Hejl: "The Common Server API is more flexible and/or more limited—depends on the point of view. It is the API/SPI to show server instances and to provide wizard to register them. Nothing more."
So, this API/SPI is not related to starting/stopping of servers. You would need to provide your own code for that. Neither is it tied to any specific server specification. Whether that makes sense for your server is up to you.
To get started with it, here's a quick and simple scenario, based on the unit tests in the "server" module in the NetBeans sources.
In the end, we'll have a new node under the "Servers" node:
We'll also have a new entry for registering a server in the Server Manager:
Plus, we'll have a wizard for setting some configurations prior to the server registration being completed:
Take the steps below:
- Register in the layer:
<folder name="Servers"> <file name="DemoInstanceProvider.instance"> <attr name="instanceClass" stringvalue="org.demo.demoserver.DemoInstanceProvider"/> <attr name="instanceOf" stringvalue="org.netbeans.spi.server.ServerInstanceProvider"/> </file> <file name="DemoWizardProvider.instance"> <attr name="instanceClass" stringvalue="org.demo.demoserver.DemoWizardProvider"/> <attr name="instanceOf" stringvalue="org.netbeans.spi.server.ServerWizardProvider"/> </file> </folder> - Now we'll create a ServerInstanceImplementation, which the provider registered above in the layer will create for us in the next step:
public final class DemoInstanceImplementation implements ServerInstanceImplementation { private final DemoInstanceProvider provider; private final String serverName; private final String instanceName; private final boolean removable; private ServerInstance serverInstance; private JPanel customizer; public DemoInstanceImplementation(DemoInstanceProvider provider, String serverName, String instanceName, boolean removable) { this.provider = provider; this.serverName = serverName; this.instanceName = instanceName; this.removable = removable; } @Override public Node getFullNode() { return new AbstractNode(Children.LEAF) { @Override public String getDisplayName() { return instanceName; } }; } @Override public Node getBasicNode() { return new AbstractNode(Children.LEAF) { @Override public String getDisplayName() { return instanceName; } }; } @Override public JComponent getCustomizer() { synchronized (this) { if (customizer == null) { customizer = new JPanel(); customizer.add(new JLabel(instanceName)); } return customizer; } } @Override public String getDisplayName() { return instanceName; } @Override public String getServerDisplayName() { return serverName; } @Override public boolean isRemovable() { return removable; } @Override public void remove() { //Here, remove the instance from the provider } } - Next, we'll define the ServerInstanceProvider, which we registered in the layer above, and which creates instances of the class in the previous step:
public class DemoInstanceProvider implements ServerInstanceProvider { @Override public List<ServerInstance> getInstances() { List<ServerInstance> instances = new ArrayList<ServerInstance>(); ServerInstance instance = ServerInstanceFactory.createServerInstance( new DemoInstanceImplementation(this, "Demo", "Demo Impl", true)); instances.add(instance); return instances; } @Override public void addChangeListener(ChangeListener listener) { } @Override public void removeChangeListener(ChangeListener listener) { } } - Finally, we also registered a ServerWizardProvider in the layer, in step 1 above, which will provide the wizard that the user will use to register the server:
public class DemoWizardProvider implements ServerWizardProvider { public DemoWizardProvider() { } @Override public String getDisplayName() { return "Demo"; } @Override public InstantiatingIterator getInstantiatingIterator() { return new DemoWizardIterator("Demo"); } private static class DemoWizardIterator implements InstantiatingIterator { private final String name; private Panel panel; public DemoWizardIterator(String name) { this.name = name; } @Override public Set instantiate() throws IOException { return Collections.EMPTY_SET; } @Override public String name() { return name; } @Override public synchronized Panel current() { if (panel == null) { panel = new DemoWizardPanel(name); } return panel; } @Override public boolean hasNext() { return false; } @Override public boolean hasPrevious() { return false; } @Override public void initialize(WizardDescriptor wizard) { } @Override public void uninitialize(WizardDescriptor wizard) { } @Override public void nextPanel() { } @Override public void previousPanel() { } @Override public void addChangeListener(ChangeListener l) { } @Override public void removeChangeListener(ChangeListener l) { } } private static class DemoWizardPanel implements Panel { private final String name; private JPanel panel; public DemoWizardPanel(String name) { this.name = name; } @Override public synchronized Component getComponent() { if (panel == null) { panel = new JPanel(); panel.add(new JLabel(name)); } return panel; } @Override public HelpCtx getHelp() { return HelpCtx.DEFAULT_HELP; } @Override public boolean isValid() { return true; } @Override public void addChangeListener(ChangeListener l) { } @Override public void removeChangeListener(ChangeListener l) { } @Override public void readSettings(Object settings) { } @Override public void storeSettings(Object settings) { } } }
That's it. You now have your first simple implementation of the NetBeans Common Server API.
Oct 07 2009, 09:29:05 AM PDT Permalink
Please suggest to phejl that annotations be offered for both kinds of registrations as a convenience. In the meantime, you could avoid creating a layer as follows:
@ServiceProvider(service=ServerInstanceProvider.class, path="Servers")
public class DemoInstanceProvider ...
@ServiceProvider(service=ServerWizardProvider.class, path="Servers")
public class DemoWizardProvider ...
This will actually create META-INF/namedservices entries, not SFS entries, but the effect should be the same since o.n.m.server.ServerRegistry uses Lookups.forPath which searches both places.
Posted by Jesse Glick on October 07, 2009 at 10:32 AM PDT #
Thank you, Geertjan! This information will be usefully for our project!
Posted by Alex on October 07, 2009 at 10:37 AM PDT #


