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
Java 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:
/etc/inetd.conf
/etc/services
inetd
to read its new
configurationThe server-side implementation uses the following source files:
ServiceInterface.java
: remote
interface for the serviceInitializeRegistry.java
:
utility to create/export a registry using an inherited channelServer.java
: service
programServiceInterface
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:
System.err
output to a file, in order to
prevent the error output from being lost in the case that the
service program is started from inetd
.InitializeRegistry.initializeWithInheritedChannel
utility method with the service's proxy, the name for the service
in the registry, and an optional port number to use if the program
is run from the command line instead of being started from
inetd
.ready
.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.javawhere classDir is the class path for this example.
The next three sections describe how to set up
inetd
to start the service program.
/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.Serverwhere 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.
/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/tcpwhere port is the port number for the service's local registry.
inetd
to read its
new configurationNow 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.ServerIf 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 inetdwhich will print out something like this:
root 171 1 0 Sep 30 ? 0:02 /usr/sbin/inetd -sIn 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 171Now,
inetd
is all set to launch the service program
when a client attempts to connect to the port configured above.
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:
ServiceInterface.java
:
remote interface for the serviceClient.java
: client
programCompile 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.