/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 * 
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits r?serv?s.
 * Ce logiciel est propri?t? de Sun Microsystems, Inc.
 * Distribu? par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)MfDiscoveryService.java 1.26     04/09/27 SMI"
 *
 */

package com.sun.mfwk.discovery;

// JESMF log
import java.util.logging.*;
import com.sun.mfwk.util.log.MfLogService;

//general stuff
import java.net.*;
import java.io.*;

// JESMF common config file
import com.sun.mfwk.config.*;

//JMX stuff
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanNotificationInfo;
import javax.management.AttributeChangeNotification;
import javax.management.ObjectName;

/**
 * 
 * The "MfDiscoveryService"  MBean is responsible to send a DISCOVERY PDU
 * everytime a CP module registers to become a notification listener.
 * It is also responsible to handle RESP PDUs emitted by each Component product
 * and to wrapp this RESP in a notification that will be sent to each of its
 * listener.
 */

public class MfDiscoveryService extends NotificationBroadcasterSupport implements MfDiscoveryServiceMBean {

    
    // This is the value that is displayed in the Config MBean as the Version
    static private final String VERSION = "1.0,REV=build 13";

   //we are guaranteed these are set even if the config file is not there
    private String myMcastGroup = null;
    private int    myMcastPort = 0; 

    private boolean disableLoopBack = false;
    private String loopBackIP = "127.0.0.1"; 
    private int TTL = 0;
    
    // Tracing utility. */
    private static Logger logger = MfLogService.getLogger("Discovery");
    
    private MfDiscoveryActualClient myDiscoveryClient = null;
    private boolean discoveryClientStarted = false;       
    
    
    
    /*
     * -----------------------------------------------------
     * CONSTRUCTORS
     * -----------------------------------------------------
     */

    public MfDiscoveryService () {
        logger.entering("MfDiscoveryService", "MfDiscoveryService");        
        //Retrieves common (Agent and CP) config parameters, i.e. mcast group & port
        MfConfig commonConfig = MfConfig.getConfig();
        myMcastGroup = commonConfig.getProperty("mfwk.multicast.group");
        myMcastPort  = (new Integer(commonConfig.getProperty("mfwk.multicast.port"))).intValue();
        disableLoopBack = (new Boolean(commonConfig.getProperty("mfwk.multicast.disableloopback"))).booleanValue();
        
        logger.config (" McastGroup = " + myMcastGroup +
                       " \nMcastPort = " + myMcastPort);                
        
        if (! discoveryClientStarted) {
            try {
		discoveryClientStarted = true;
        
		// creates responder
		myDiscoveryClient = new MfDiscoveryActualClient (myMcastGroup, myMcastPort, this);

		//set TTL = 0 => msg does not go out
		myDiscoveryClient.setTimeToLive(TTL);

                //Ensures loopback mode is enable (i.e. we want to receive what is sent on the loopback...)
                myDiscoveryClient.setLoopbackMode(false);

                //forces to use the loopback interface (whose IP is given as a parameter)
                //NB default behavior is to use loopBack (enter the if{}),
                //using default interface alternative has been added for solaris 2.8 only
                //BugId 5065408 & BugId 5025737
                if (!disableLoopBack) {
                  InetAddress loopBack = InetAddress.getByName(loopBackIP);
                  myDiscoveryClient.setInterface(loopBack);                 
                }
                logger.finer("interface used: " + myDiscoveryClient.getNetworkInterface().getName());                


                logger.finer("joins multicast group");                
		myDiscoveryClient.connectToGroup();

                logger.finer("start Listening Thread");                
		myDiscoveryClient.startListeningThread();		    
	    
            } catch (IOException ioe) {
                logger.warning("Problem occured while creating discovery client: " + ioe.getMessage());
            }
        }
        logger.exiting("MfDiscoveryService", "MfDiscoveryService");        
    }
        


    /*
     * -----------------------------------------------------
     * METHOD NOT EXPOSED FOR MANAGEMENT BY A JMX AGENT
     * -----------------------------------------------------
     */

    // Overload addNotificationListener as we want to send a
    // Discovery message everytime a CP module registers as
    // a notification listener

    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
        logger.entering("MfDiscoveryService", "addNotificationListener", new Object[]{listener,filter,handback});        
	// registers the notification listener
	super.addNotificationListener(listener,filter,handback);

	try {
	
	    // creates DISCOVERY message
	    MfMcastMessage discMessage = new MfMcastMessage(MfMcastMessage.DISCOVERY,
                                                        0,
                                                        "no Product",
                                                        "no Product Instance", 
                                                        "no uri", 
                                                        null, 
                                                        false, 
                                                        null, 
                                                        false);

            //sends DISCOVERY Message
	    byte myPacketByteArray[];
	    myPacketByteArray = discMessage.getByteArrayMcastMessage();  
	    DatagramPacket myPacket = new DatagramPacket(myPacketByteArray,myPacketByteArray.length, InetAddress.getByName(myMcastGroup), myMcastPort);
	    myDiscoveryClient.send(myPacket);
	} catch (IOException ioe) {
           logger.warning("Problem occured while emitting discovery frame: " + ioe.getMessage());
	}
        logger.exiting("MfDiscoveryService", "addNotificationListener");        
    }


    public void stop() {
        logger.entering("MfDiscoveryService","stop");
        myDiscoveryClient.stopListeningThread();
        logger.exiting("MfDiscoveryService","stop");

    }

    //BS TB cleaned up
    /**    
     * Returns a NotificationInfo object containing the name of the Java class of the notification
     * and the notification types sent by this notification broadcaster.  
     */
    
    public MBeanNotificationInfo[] getNotificationInfo() {
        logger.entering("MfDiscoveryService", "getNotificationInfo");        
        MBeanNotificationInfo[] ntfInfoArray  = new MBeanNotificationInfo[1];
        
        String[] ntfTypes = new String[1];
        ntfTypes[0] = AttributeChangeNotification.ATTRIBUTE_CHANGE;
        
        ntfInfoArray[0] = new MBeanNotificationInfo(ntfTypes,
                                                    "javax.management.AttributeChangeNotification", 
                                                    "Attribute change notification for the 'State' attribute.");
        logger.exiting("MfDiscoveryService", "getNotificationInfo",ntfInfoArray);        
        return ntfInfoArray;
    }  
    
    public String getMulticastGroup() {
        logger.entering("MfDiscoveryService", "getMulticastGroup");        
        logger.exiting("MfDiscoveryService", "getMulticastGroup");        
        return myMcastGroup;
    }
    
    public int getMulticastPort() { 
        logger.entering("MfDiscoveryService", "getMulticastPort");        
        logger.exiting("MfDiscoveryService", "getMulticastPort");        
        return myMcastPort;
    }

    public String getVersion() {
        return VERSION;
    }
}
