/*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* -Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Oracle nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
package example.inetd;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.Channel;
import java.nio.channels.ServerSocketChannel;
import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIServerSocketFactory;
/**
* A utility to export a registry (using an inherited channel if launched
* from inetd
) and to bind a remote service's proxy in that
* registry for clients to look up.
**/
public class InitializeRegistry {
private static final Object lock = new Object();
private static boolean serviceAvailable = false;
private static boolean initialized = false;
/**
* Prevents instantiation.
*/
private InitializeRegistry() {
throw new AssertionError();
}
/**
* Creates and exports a registry (using an inherited channel, if any,
* as specified below), and binds the specified name to the specified
* proxy in that registry.
*
* First, the registry is exported as follows:
*
System.inheritedChannel
method returns a
* ServerSocketChannel
instance, the registry is exported
* with an RMIServerSocketFactory
whose
* createServerSocket
method returns a
* ServerSocket
for the inherited
* ServerSocketChannel
that delays accepting
* requests until the specified proxy is bound in the
* registry.
*
* System.inheritedChannel
method returns
* null
, then the registry is exported with an
* RMIServerSocketFactory
whose
* createServerSocket
method returns a
* ServerSocket
constructed with the specified
* port that delays accepting requests until the specified proxy is
* bound in the registry. In this case, if the port is 0
,
* then an IllegalArgumentException
is thrown.
*
* System.inheritedChannel
returns
* an instance of any other type, an IOException
is
* thrown.
* Once the registry is exported, the registry's bind
* method is invoked with the specified name and proxy as arguments.
*
* @param obj the proxy for a remote object
* @param name the name for the remote object in the registry
* @param port a port to export the registry on if there is no
* inherited channel
* @throws IllegalArgumentException if the inherited channel is
* null
and the port is 0
* @throws IllegalStateException if this method was called previously
* @throws IOException if the inherited channel is not an instance
* of ServerSocketChannel
or null
* @throws RemoteException if the registry could not be exported
**/
public static void initializeWithInheritedChannel(Remote proxy,
String name,
int port)
throws IOException
{
/*
* Only allow this method to be invoked once.
*/
synchronized (InitializeRegistry.class) {
if (initialized) {
throw new IllegalStateException("already invoked");
}
initialized = true;
}
Channel channel = System.inheritedChannel();
ServerSocket serverSocket = null;
/*
* Handle inherited channel, if any.
*/
if (channel instanceof ServerSocketChannel) {
/*
* Service launched from inetd. Get server socket from
* inherited server socket channel.
*/
serverSocket = ((ServerSocketChannel) channel).socket();
} else if (channel == null) {
/*
* Service launched from the command line. In this case, the
* port specified for the registry must be nonzero
*/
if (port == 0) {
throw new IllegalArgumentException("port must be nonzero");
}
serverSocket = new ServerSocket(port);
} else {
throw new IOException(
"unexpected channel returned from inheritedChannel: " +
channel.toString());
}
/*
* Create server socket factory for registry to delay accepting
* calls until a service is bound in the registry.
*/
RMIServerSocketFactory ssf =
new RegistryServerSocketFactory(serverSocket);
/*
* Create/export registry and bind name to proxy in registry.
*/
Registry registry = LocateRegistry.createRegistry(port, null, ssf);
try {
registry.bind(name, proxy);
} catch (RemoteException impossible) {
throw new AssertionError(impossible);
} catch (AlreadyBoundException impossible) {
throw new AssertionError(impossible);
}
/*
* Notify registry's socket factory that the service proxy is
* bound in the registry, so that the registry can accept
* incoming requests to look up the service.
*/
synchronized (lock) {
serviceAvailable = true;
lock.notifyAll();
}
}
/**
* A server socket factory to use when exporting a registry launched
* from 'inetd' with 'wait' status. This socket factory's
* 'createServerSocket' method returns a server socket that wraps the
* server socket specified during construction and is specialized to
* delay accepting requests until a remote service is bound in the
* registry (when the 'serviceAvailable' flag is 'true'). The server
* socket supplied to the constructor should be the server socket
* obtained from the 'System.inheritedChannel' method.
*
* Note that only a single instance of this class should be created.
**/
private static class RegistryServerSocketFactory
implements RMIServerSocketFactory
{
private final ServerSocket serverSocket;
/**
* Constructs a 'RegistryServerSocketFactory' with the specified
* 'serverSocket'.
**/
RegistryServerSocketFactory(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
/**
* Returns the server socket specified during construction wrapped
* in a 'DelayedAcceptServerSocket'. The port argument is ignored.
**/
public ServerSocket createServerSocket(int port)
throws IOException
{
return new DelayedAcceptServerSocket(serverSocket);
}
}
/**
* A server socket that delegates all public methods to the underlying
* server socket specified at construction. The accept method is
* overridden to delay calling accept on the underlying server socket
* until a remote service is bound in the registry (when the
* 'serviceAvailable' flag is 'true').
**/
private static class DelayedAcceptServerSocket extends ServerSocket {
private final ServerSocket serverSocket;
/**
* Constructs a 'DelayedAcceptServerSocket' with the specified
* 'serverSocket'.
*/
DelayedAcceptServerSocket(ServerSocket serverSocket)
throws IOException
{
this.serverSocket = serverSocket;
}
public void bind(SocketAddress endpoint) throws IOException {
serverSocket.bind(endpoint);
}
public void bind(SocketAddress endpoint, int backlog)
throws IOException
{
serverSocket.bind(endpoint, backlog);
}
public InetAddress getInetAddress() {
return serverSocket.getInetAddress();
}
public int getLocalPort() {
return serverSocket.getLocalPort();
}
public SocketAddress getLocalSocketAddress() {
return serverSocket.getLocalSocketAddress();
}
/**
* Delays calling accept on the underlying server socket until the
* remote service is bound in the registry.
**/
public Socket accept() throws IOException {
synchronized (lock) {
try {
while (!serviceAvailable) {
lock.wait();
}
} catch (InterruptedException e) {
throw (IOException)
(new InterruptedIOException()).initCause(e);
}
}
return serverSocket.accept();
}
public void close() throws IOException {
serverSocket.close();
}
public ServerSocketChannel getChannel() {
return serverSocket.getChannel();
}
public boolean isBound() {
return serverSocket.isBound();
}
public boolean isClosed() {
return serverSocket.isClosed();
}
public void setSoTimeout(int timeout)
throws SocketException
{
serverSocket.setSoTimeout(timeout);
}
public int getSoTimeout() throws IOException {
return serverSocket.getSoTimeout();
}
public void setReuseAddress(boolean on) throws SocketException {
serverSocket.setReuseAddress(on);
}
public boolean getReuseAddress() throws SocketException {
return serverSocket.getReuseAddress();
}
public String toString() {
return serverSocket.toString();
}
public void setReceiveBufferSize(int size)
throws SocketException
{
serverSocket.setReceiveBufferSize(size);
}
public int getReceiveBufferSize()
throws SocketException
{
return serverSocket.getReceiveBufferSize();
}
}
}