Equinox Headless Bootstrap

Following lots of research and comments about how to run the Equinox OSGi system headless as a service, I've decided that the best thing to do is create a Bootstrap class similar to that used by Apache Tomcat.

I will build this in two phases. This page serves as a basic outline of the project.

Project Home

Due to bandwidth contraints on my host, I'm keeping the project over at google code.

Equinox Headless Service - Google Code

The Overall Objective

In the end, I hope to build the OSGi application stack recently described on my blog.

Small Server Stack

You might say, the headless startup scripts are but the first part.

Phase 1: Simple Start-up / Shut-down script

In the first phase, I'll create a simple Bootstrap class that starts the Equinox service and listens on a port for commands. The commands that it will accept are "STATUS" (to indicate if it is running) and "SHUTDOWN" (to perform a clean shutdown of the Equinox service).

The basic layout of the system will be: /etc/init.d/equinox_bootstrap The system start-up /shut-down script. /etc/equinox_bootstrap/bootstrap.conf This properties file will contain the admin port number and well as specify the locations of the other components. /var/equinox This will be the "run from" directory. The configuration directory used by Equinox should live here, as well as other dependencies (ie. the equinox.jar file).

A rough draft for the implementation of the Bootstrap class follows (keep in mind, the equinox.jar file will need to be on the classpath if using this): Though this class is functional, much is left out in order to get the point accross. Grab the source from google code to see the latest.

package com.willcode4beer.service.equinox;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.adaptor.EclipseStarter;

public class Bootstrap {

  private volatile boolean running;
  private int adminPort = 9005; // default port

  public static void main(String[] args) {
    Bootstrap runner = new Bootstrap();
    try {
      runner.start();
    } catch (Exception e) {
      System.exit(2);
    } finally {
      try {
        runner.shutDown();
      } catch (Exception e) {
        System.exit(2);
      }
    }
    System.exit(0);
  }

  private void start() throws Exception {
    startEquinox();
    this.running = true;
    startAdmin();
  }

  public void shutDown() throws Exception {
    this.running = false;
    EclipseStarter.shutdown();
  }

  public boolean status() {
    return EclipseStarter.isRunning();
  }

  private void startEquinox() throws Exception {
    Map<String, String> props = new HashMap<String, String>();
    props.put("eclipse.ignoreApp", "true");
    props.put("osgi.noShutdown", "true");
    EclipseStarter.setInitialProperties(props);
    EclipseStarter.startup(new String[] {}, null);
  }

  private void startAdmin() throws Exception {
    ServerSocket ss = new ServerSocket(adminPort);
    while (running) {
      Socket sock = ss.accept();
      InputStream in = sock.getInputStream();
      BufferedReader reader = new BufferedReader(
          new InputStreamReader(in));
      String command = reader.readLine();
      OutputStream out = sock.getOutputStream();
      if ("SHUTDOWN".equals(command)) {
        sendResponse(out, "shutting down");
        shutDown();
        sendResponse(out, "stopped");
      } else if ("STATUS".equals(command)) {
        String response = (status() ? "running" : "not running");
        sendResponse(out, response);
      }
      reader.close();
      in.close();
      out.close();
    }
  }

  private void sendResponse(OutputStream out, String response)
      throws IOException {
    PrintWriter writer = new PrintWriter(out);
    writer.println(response);
    writer.flush();
  }

}

The draft version can be tested using netcat

echo "STATUS" | nc localhost 9005

Will indicate if the service is running.

echo "SHUTDOWN" | nc localhost 9005

Will shut the service down.

Updated: On the Google Code page for this project, I have a tar/gzip file with a better implementation. It also contains a start-up script to go in /etc/init.d and a config file for /etc to define the port and directory locations.

The complete source can also be found on the Google Code page.

Let's use Google Issue tracker for any problems.

Phase 2: JMX Administration

This phase will introduce a very basic system of administering the Equinox service using JMX. It will have the ability to, start, stop, install, uninstall, and check status of OSGi bundles running in the Equinox service.

Because installing bundles is a bit time consuming, I will also create a set of shell scripts to perform these tasks as well. This will allow scripting OSGi deployments (especially when there are lots of dependencies).

The other benefit of using JMX is remote administration. I may or may not build a JMX tool to control clusters of remote servers, it really just depends upon what my needs are. If you are interested, feel free to contact me.

Right now, I'm of two minds regarding JMX administration. On the one hand, I really like the idea of just making it an OSGi bundle. OTOH, this requires installing the bundle, increasing set-up time. So, the other option is to kick it off with the Bootstrap class. Then again, the bootstrap could just have an option to auto-magically load and start the JMX admin bundle.

Resources

Tech Tags:


Sponsors:

About willCode4Beer