X

Geertjan's Blog

  • October 7, 2009

NetBeans Common Server API

Geertjan Wielenga
Product Manager
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:


  1. 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>

  2. 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
    }
    }

  3. 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) {
    }
    }

  4. 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.

Join the discussion

Comments ( 2 )
  • Jesse Glick Wednesday, October 7, 2009

    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.


  • Alex Wednesday, October 7, 2009

    Thank you, Geertjan! This information will be usefully for our project!


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