/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment 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://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * 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.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.norm;

import java.util.logging.Level;
import java.util.logging.Logger;

import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.lease.DesiredExpirationListener;
import net.jini.lease.LeaseRenewalEvent;

/**
 * Object that transfers events from the Lease Renewal Manager to the
 * rest of Norm Server.  Isolates the renewal manager from having to
 * block on the snapshot locks.
 *
 * @author Sun Microsystems, Inc.
 */
class LRMEventListener extends Thread implements DesiredExpirationListener {
    /** Logger for logging messages for this class */
    private static final Logger logger = Logger.getLogger("com.sun.jini.norm");

    /** Ref to the main server object which has all the top level methods */
    final private NormServerBaseImpl server;

    /**
     * Queue we use to decouple the reception of events from the lease
     * renewal manager from the scheduling of the sending of remote
     * events and modifying our internal tables (which both require
     * obtaining serious locks).
     */
    final private Queue queue = new Queue();

    /** Any events that hold this object are ignored */
    final static LeaseDeniedException EXPIRED_SET_EXCEPTION = 
	new LeaseDeniedException("Set Expired");
	
    /**
     * Simple constructor
     *
     * @param server Object that will make the actual internal updates and
     * schedule the sending of remote events
     */
    LRMEventListener(NormServerBaseImpl server) {
	super("LRM Event Listener");
	setDaemon(true);
	this.server = server;	
    }

    //////////////////////////////////////////////////
    // Methods required by the LeaseListener interface 

    // Inherit java doc from super type
    public void notify(LeaseRenewalEvent e) {	
	// Drop if the exception field is == to EXPIRED_SET_EXCEPTION, this
	// implies that lease could not be renewed because the wrapper 
	// has determined that the set has expired.
	if (e.getException() == EXPIRED_SET_EXCEPTION) 
	    return;

	// Paranoia, check to make sure that lease is one of wrapped
	// client lease...if it's not, ignore the event
	final Lease l = e.getLease();
	if (l instanceof ClientLeaseWrapper) {
	    final ClientLeaseWrapper clw = (ClientLeaseWrapper) l;
	    queue.enqueue(new Discriminator(clw, true));
	}
    }

    //////////////////////////////////////////////////////////////
    // Methods required by the DesiredExpirationListener interface
	
    // Inherit java doc from super type
    public void expirationReached(LeaseRenewalEvent e) {
	// Paranoia, check to make sure that lease is one of wrapped
	// client lease...if it's not, ignore the event
	final Lease l = e.getLease();
	if (l instanceof ClientLeaseWrapper) {
	    final ClientLeaseWrapper clw = (ClientLeaseWrapper) l;
	    queue.enqueue(new Discriminator(clw, false)) ;
	}
    }

    public void run() {
	// Loop taking items off the queue and pass them to the server
	while (!isInterrupted()) {
	    try {
		final Discriminator d = (Discriminator) queue.dequeue();

		if (d.isFailure) {
		    server.renewalFailure(d.clw);
		} else {
		    server.desiredExpirationReached(d.clw);
		}

	    } catch (InterruptedException e) {
		// Someone wants this thread dead -- just return
		return;
	    } catch (RuntimeException e) {
		logger.log(Level.INFO,
			   "Exception in LRMEventListener Notifier while " +
			   "processing an event from the LRM -- " +
			   "attempting to continue",
			   e);

	    } catch (Error e) {
		logger.log(Level.INFO,
			   "Exception in LRMEventListener Notifier while " +
			   "processing an event from the LRM -- " +
			   "attempting to continue",
			   e);
	    }
	}
    }

    /**
     * Trivial container class to tell us if we are processing the given
     * wrapper because of a failure event or a desired expiration
     * reached event.
     */
    static private class Discriminator {
	/** true if this wrapper is associated with a renewal failure event */
	final private boolean isFailure;

	/** The wrapped leases associated with the event */
	final private ClientLeaseWrapper clw;

	private Discriminator(ClientLeaseWrapper clw, boolean isFailure) {
	    this.isFailure = isFailure;
	    this.clw = clw;
	}
    }
}
