Designing Services to be Launched from inetd

On the Solaris Operating System (Solaris OS), the Internet services daemon inetd provides an alternative to starting up services at system boot time. This daemon, a server process for Internet standard services, can be configured to start services on demand. For details on the Internet services daemon, see the Solaris OS inetd(1M) man page.

Now, inetd can be configured to start JavaTM Remote Method Invocation (Java RMI) services on demand. An application, however, must use a specific technique to enable that application and its constituent services to be started from inetd. First, the service program should create a local registry, exported to use the I/O socket inherited from inetd. The service program should then bind the service's proxy in this specially exported registry so that clients can look up the service. Once the service program is complete, inetd can be configured to start this program when a client attempts to connect to the service's local registry in order to look up the service by name.

This tutorial first describes how to structure a service program (employing a specially exported local registry) so that the service can be started from inetd when clients connect to the service's local registry.

Next, the tutorial describes how to configure inetd to launch the service program. You will need to add an entry to each of two configuration files that inetd uses, /etc/inetd.conf and /etc/services. Editing these files requires root access to the machine that the service will run on.

After inetd has been reconfigured, you should test your configuration to make sure that it works properly.

This tutorial has the following steps:


Implement the service program

The server-side implementation uses the following source files:

The interface ServiceInterface defines a remote interface with a single method sendMessage specified to take a single argument, message, and return an acknowledgment that the message was received.

The class InitializeRegistry defines a static utility method initializeWithInheritedChannel that creates and exports a registry (using an inherited channel, if any), and binds a remote service's proxy in that registry for clients to look up.

The service program Server implements the interface ServiceInterface and defines a static main method for running the service. The static main method does the following:

The most interesting part of the implementation is in the initializeWithInheritedChannel utility method. This method uses the System.inheritedChannel method which allows an application to obtain a channel (java.nio.channels.SocketChannel or java.nio.channels.ServerSocketChannel, for example) inherited from a process that launched the virtual machine. Such an inherited channel can be used to either service a single incoming connection (in the case of a SocketChannel), or accept multiple incoming connections (in the case of ServerSocketChannel). In this way, applications launched by inetd can obtain the SocketChannel or ServerSocketChannel inherited from inetd.

The initializeWithInheritedChannel utility method first invokes the System.inheritedChannel method to obtain the inherited channel. The inherited channel must either be null or a ServerSocketChannel or the method will throw an IOException.

If the inherited channel is null, there is no inherited channel; that is, the program was run from the command line. In this case, the initializeWithInheritedChannel method simply exports a registry on the specified port (which must be nonzero) and binds the specified service proxy in that registry.

If the inherited channel is a ServerSocketChannel instance, then the program was started from inetd. In this case, the initializeWithInheritedChannel method exports a registry with an RMIServerSocketFactory whose createServerSocket method returns a ServerSocket that delays accepting requests from the inherited ServerSocketChannel until the specified proxy is bound in the registry.

In the case that the program is started from inetd, it is important that the registry not accept any incoming connections on the inherited ServerSocket until the service's proxy is bound in the local registry. If a connection is accepted before the service is bound in the registry, a client might receive a java.rmi.NotBoundException from attempting to look up the service proxy.

For details on how to implement a ServerSocket that delays accepting requests until a service is bound in the registry, see the private nested class DelayedAcceptServerSocket defined in the InitializeRegistry class.

Compile the service program as follows:

% javac -d classDir ServiceInterface.java InitializeRegistry.java Server.java
where classDir is the class path for this example.

Note that if the service needs to be accessible to clients running on releases earlier than J2SE 5.0, you will need to use rmic to generate a stub for the remote service.

The next three sections describe how to set up inetd to start the service program.

Configure /etc/inetd.conf

The /etc/inetd.conf configuration file contains entries for the services to be launched when inetd receives a request over a socket. For details on the format of this configuration file see the Solaris OS inetd.conf(4) man page.

To configure inetd to launch the service program, add the following entry to the /etc/inetd.conf configuration file (requires root access to the machine):

example-server stream tcp wait nobody jreHome/bin/java  \
     java -classpath classpath example.inetd.Server
where jreHome is a path to the installed JRE, and classpath is the class path for the example.

If the program needs to be run as a user other than nobody, replace nobody above with the user ID under which the program should run.

Configure /etc/services

Next, the name chosen to refer to the service, example-server, needs to be listed as a service in the /etc/services configuration file. For details on the format of this configuration file see the Solaris OS services(4) man page.

To list example-server as a service, add the following entry to the /etc/services configuration file (requires root access to the machine):

example-server        port/tcp
where port is the port number for the service's local registry.

Force inetd to read its new configuration

Now that the configuration has been modified, inetd needs to read the new configuration so that it can listen on the appropriate ports for the services that are configured.

But first, it is important to make sure that the service program is not already running. To do this, run the following command:

% ps -ef | grep example.inetd.Server
If the above command does not print out the information for a running java process for the service program, then the program is not running. If it does, then you will need to terminate the program first before continuing.

Next, inetd needs to read its new configuration. To force inetd to reread its configuration, the running inetd process must be sent a hangup signal. First, find the process ID for the running inetd process by running the following command:

% ps -ef | grep inetd
which will print out something like this:
root   171     1  0   Sep 30 ?        0:02 /usr/sbin/inetd -s
In this example, the process ID for inetd is 171. Now, the inetd process can be sent a hangup signal by running the following command and supplying the process ID (requires root access):
% kill -HUP 171
Now, inetd is all set to launch the service program when a client attempts to connect to the port configured above.

Test your configuration using a simple client

To test that inetd is configured properly, you can run a simple client program that looks up the service in the registry on the port configured above, and then invokes a method on the service. Attempting to connect to the service's local registry will, in turn, cause inetd to launch the service program if the configuration is correct.

The following is a simple program to look up the service and invoke a method on the service to send it a message. The program takes three command-line arguments, the host and port of the service's local registry, and the message to send to the service:

package example.inetd;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {

    public static void main(String[] args) throws Exception {

        int port = 0;
        String host = "";
        String message = "";
        
        if (args.length > 2) {
            host = args[0];
            try {
                port = Integer.parseInt(args[1]);
                if (port == 0) {
                    goodbye("nonzero port argument required", null);
                }
            } catch (NumberFormatException e) {
                goodbye("malformed port argument", e);
            }
            message = args[2];
        } else {
            usage();
        }

        Registry registry = LocateRegistry.getRegistry(host, port);
        ServiceInterface proxy =
            (ServiceInterface) registry.lookup("ServiceInterface");
        System.out.println("sending message: " + message);
        System.out.println("received message from proxy: " +
                           proxy.sendMessage(message));
        
        System.out.println("done.");
    }

    private static void goodbye(String message, Exception e) {
        System.err.println("Client: " + message + (e != null ? ": " : ""));
        if (e != null) {
            e.printStackTrace();
        }
        System.exit(1);
    }

    private static void usage() {
        System.err.println("Client <host> <port> <message>");
        System.exit(1);
    }
}

The full source (including comments) for the client is:

Compile and run this program as follows:

% javac -d classDir ServiceInterface.java Client.java
% java -classpath classDir example.inetd.Client host port "message"
where classDir is the class path for this example, host is the host that the service is configured to run on, port is the port configured for the service in the /etc/services file, and message is a string to send to the service.

If the client program prints out that it sent a message to and received a message from the service, then the service program was successfully launched by inetd.

If the client program either hangs or prints an exception trace, check the output produced by the service program. The service program redirects any output written to System.err to a file located in the directory specified by the property java.io.tmpdir, typically /var/tmp on the Solaris OS. The output file is prefixed with "example-server-err" and has the suffix of ".tmp". The file name also contains other characters (typically numbers) in between to keep the file name unique.

If the service program starts up successfully from inetd, the output file should contain the text "ready" with no warning or error messages.

If the file does not exist, the "ready" message is not in the file, or additional error output is in the file, then recheck the configuration. Upon changing the inetd configuration, remember to send inetd a hangup signal so that it rereads its altered configuration, and remember to terminate any process started from the previous attempt before continuing.


Oracle and/or its affiliates
Java Technology

Copyright © 1993, 2011, Oracle and/or its affiliates. All rights reserved.

Contact Us