/*
 * ident	"@(#)DAAdvertiser.java	1.2	99/05/10 SMI"
 *
 * Copyright (c) 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 *
 */

//  SCCS Status:      @(#)DAAdvertiser.java	1.2	05/10/99
//  DAAdvertiser.java: Advertise a DA, also handle incoming DAAdverts.
//  Author:           James Kempf
//  Created On:       Tue May 19 15:22:04 1998
//  Last Modified By: James Kempf
//  Last Modified On: Thu Mar  4 10:39:06 1999
//  Update Count:     44
//

package com.sun.slp;

import java.net.*;
import java.util.*;
import java.io.*;

/**
 * This class supplies a regular interval 'heartbeat' DAAdvertisement.
 * Implementation specific subclasses handle incoming DAAdverts and
 * forwarding of registrations and deregistrations to other DAs. The
 * implementation specific subclasses depend on how the server is
 * representing DA information internally.
 *
 * @version 1.2 00/10/14
 * @author James Kempf, Erik Guttman
 */

class DAAdvertiser extends Thread {

    protected DatagramSocket dss = null;
    protected InetAddress castAddr = null;
    protected InetAddress interfac = null;

    // V2 advertising has the same DAAdvert every time.

    static private byte[]	 outbuf = null;

    static protected SLPConfig config = null;    // Config object.
    static protected Hashtable daadv =
	new Hashtable();	// Existing advertisers

    private Boolean done = new Boolean(false);

    // Initialize the DAAdvertiser on the designated interface.

    static void initializeDAAdvertiserOnInterface(InetAddress interfac)
	throws ServiceLocationException {

	// If we've got it, return.

	if (daadv.get(interfac) != null) {
	    return;

	}

	// Get the config object.

	if (config == null) {
	    config = SLPConfig.getSLPConfig();

	}

	// Get the SLPv2 DAADvert to send

	ServiceTable table = ServiceTable.getServiceTable();

	SLPServerHeaderV2 hdr =
	    new SLPServerHeaderV2(SrvLocHeader.DAAdvert,
				  false,
				  config.getLocale());

	SDAAdvert msg =
	    (SDAAdvert)table.makeDAAdvert(hdr,
					  interfac,
					  (short)0x0,
					  config.getSAConfiguredScopes(),
					  config);

	// Create a new DAAdvertiser for this interface, with SLPv2
	//  message to send.

	DAAdvertiser daadv = new DAAdvertiser(interfac, msg.getHeader());

	// Start thread running.

	daadv.start();

    }

    // Used by V1 subclass constructor.

    DAAdvertiser() {

	if (config == null) {
	    config = SLPConfig.getSLPConfig();

	}

    }

    // Create a new DAAdvertiser for the interface for default multicast
    //  address. Externalize the message and set the instance variable.

    DAAdvertiser(InetAddress interfac, SrvLocHeader hdr)
	throws ServiceLocationException {

	// Config may not be initialized if this was called directly from
	//  slpd.

	if (config == null) {
	    config = SLPConfig.getSLPConfig();

	}

	// Externalize the DAAdvert.

	ByteArrayOutputStream baos = new ByteArrayOutputStream();

	hdr.externalize(baos, true, false);
	outbuf = baos.toByteArray();

	// Initialize the networking for default multicast address.

	initializeNetworking(interfac, config.getMulticastAddress());

    }


    // Convert advert to bytes, initialize networking.

    protected void
	initializeNetworking(InetAddress interfac,
			     InetAddress maddr)
	throws ServiceLocationException {

	// Set the interface and multicast address on this advertiser.

	this.interfac = interfac;
	this.castAddr = maddr;

	// Get the socket from the listener object corresponding to this
	//  interface. The listener will always be started before the
	//  DAAdvertiser, otherwise, SAs may start sending registrations
	//  before anybody is listening.

	dss = Listener.returnListenerSocketOnInterface(interfac);

	// If the socket is null, then there is no listener. Open a
	//  new socket. Note that any listener opened *afterwards* will
	//  not get this socket.

	if (dss == null) {
	    dss = config.getMulticastSocketOnInterface(interfac, true);

	}
    }

    public void run() {

	// Set the thread name.

	setName("SLP DA Advertisement");

	long heartbeat = config.getAdvertHeartbeatTime() * 1000;

	while (true) {

	    // Send an advert.

	    sendAdvert();

	    // Sleep until next time.

	    try {
		sleep(heartbeat);

	    } catch (InterruptedException ie) {

		// Somebody interrupted us. If we are to exit, then do so.

		synchronized (done) {

		    if (done.booleanValue()) {
			return;

		    }
		}

	    }

	}
    }

    // Send an unsolicited DAAdvert.

    void sendAdvert() {

	byte[] buf = getOutbuf();

	DatagramPacket dp =
	    new DatagramPacket(buf,
			       buf.length,
			       castAddr,
			       Defaults.iSLPPort);
	try {

	    dss.send(dp);

	} catch (IOException ex) {
	    config.writeLog("passive_advert_exception",
			    new Object[] {ex.getMessage()});

	    // Tell the listener to refresh the socket.

	    dss = Listener.refreshSocketOnInterface(interfac);

	}
    }

    // Return the buffer for transmission.

    protected byte[] getOutbuf() {
	return outbuf;

    }

    // Stop the thread from executing.

    void stopThread() {

	synchronized (done) {
	    done = new Boolean(true);

	}

	this.interrupt();

    }
}
