/*
 * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source License
 * Jini Technology Core Platform, v.1.0 (the "License"); you may not use
 * this file except in compliance with the License.  You may obtain a copy of
 * the License at http://java.sun.com/products/jini. Software distributed
 * under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
 * ANY KIND, either express or implied.  See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * CopyrightVersion v1.0_Jini
 */
package net.jini.core.discovery;

import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.net.MalformedURLException;
import java.net.Socket;
import java.security.AccessController;
import java.security.PrivilegedAction;
import net.jini.core.lookup.ServiceRegistrar;

/**
 * A utility class that performs unicast discovery.
 *
 * @author Bryan O'Sullivan
 */
public class LookupLocator implements Serializable {
    private static final long serialVersionUID = 1448769379829432795L;

    /**
     * The port for both unicast and multicast boot requests.
     */
    private static final short discoveryPort = 4160;
    /**
     * The current version of the unicast discovery protocol.
     */
    private static final int protoVersion = 1;

    /**
     * The name of the host at which to perform discovery.
     *
     * @serial
     */
    protected String host;
    /**
     * The port number on the host at which to perform discovery.
     *
     * @serial
     */
    protected int port;
    
    /**
     * The timeout after which we give up waiting for a response from
     * the lookup service.
     */
    static final int defaultTimeout =
	((Integer)AccessController.doPrivileged(new PrivilegedAction() {
	    public Object run() {
		int timeout = 60 * 1000;
		try {
		    return Integer.getInteger("net.jini.discovery.timeout",
					      timeout);
		} catch (SecurityException e) {
		    return new Integer(timeout);
		}
	    }
	})).intValue();

    /**
     * Construct a new LookupLocator object, set up to perform
     * discovery to the given URL. 

* * The URL in question must be of the form jini://host/ * or jini://host:port/. * * @param url the URL to use * @exception MalformedURLException the URL passed in could not be * parsed */ public LookupLocator(String url) throws MalformedURLException { if (!url.toLowerCase().startsWith("jini://")) throw new MalformedURLException("not a jini-scheme URL"); int len = url.length(); char c = '\0'; int h; for (h = 7; h < len && (c = url.charAt(h)) != ':' && c != '/'; h++) { // do nothing } if (h == 7) throw new MalformedURLException("empty hostname"); host = url.substring(7, h); if (c == ':') { int p; for (p = h + 1; p < len && (c = url.charAt(p)) != '/'; p++) { // do nothing } try { port = Integer.parseInt(url.substring(h + 1, p)); if (port <= 0 || port >= 65536) throw new MalformedURLException("port number out of range"); } catch (NumberFormatException e) { throw new MalformedURLException("malformed port number"); } } else { port = discoveryPort; } } /** * Construct a new LookupLocator object, set to perform unicast * discovery to the given host and port. * * @param host the name of the host to contact * @param port the number of the port to connect to */ public LookupLocator(String host, int port) { if (host == null) throw new NullPointerException("null host"); if (port <= 0 || port >= 65536) throw new IllegalArgumentException("port number out of range"); this.host = host; this.port = port; } /** * Return the name of the host this LookupLocator object should * contact. */ public String getHost() { return host; } /** * Return the number of the port to which this LookupLocator * object should connect. */ public int getPort() { return port; } /** * Perform unicast discovery and return the ServiceRegistrar * object for the given lookup service. Unicast discovery is * performed anew each time this method is called. * * @return the ServiceRegistrar for the lookup service denoted by * this LookupLocator object * @exception IOException an error occurred during discovery * @exception ClassNotFoundException some discovery-related * classes could not be found */ public ServiceRegistrar getRegistrar() throws IOException, ClassNotFoundException { return getRegistrar(defaultTimeout); } /** * Perform unicast discovery and return the ServiceRegistrar * object for the given lookup service. Unicast discovery is * performed anew each time this method is called.

* * If a connection can be established to start unicast discovery * but the remote end fails to respond within the given time * limit, an exception is thrown. * * @param timeout the maximum time to wait for a response, in * milliseconds * @return the ServiceRegistrar for the lookup service denoted by * this LookupLocator object * @exception IOException an error occurred during discovery * @exception ClassNotFoundException some discovery-related * classes could not be found */ public ServiceRegistrar getRegistrar(int timeout) throws IOException, ClassNotFoundException { Socket sock = new Socket(host, port); try { sock.setSoTimeout(timeout); DataOutputStream dstr = new DataOutputStream(sock.getOutputStream()); dstr.writeInt(protoVersion); dstr.flush(); ObjectInputStream istr = new ObjectInputStream(sock.getInputStream()); ServiceRegistrar registrar = (ServiceRegistrar)((MarshalledObject)istr.readObject()).get(); for (int grpCount = istr.readInt(); --grpCount >= 0; ) { istr.readUTF(); // ensure proper format, then discard } return registrar; } finally { try { sock.close(); } catch (IOException e) { // ignore } } } /** * Return the string form of this LookupLocator, as a * jini-scheme URL. */ public String toString() { if (port != discoveryPort) return "jini://" + host + ":" + port + "/"; return "jini://" + host + "/"; } /** * Two locators are equal if they have the same host and port. */ public boolean equals(Object o) { if (o instanceof LookupLocator) { LookupLocator oo = (LookupLocator) o; return port == oo.port && host.equalsIgnoreCase(oo.host); } return false; } public int hashCode() { return host.toLowerCase().hashCode() ^ port; } }