/*
 * 
 * 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.landlord;

import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.rmi.RemoteException;

import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseMap;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.id.Uuid;
import net.jini.id.ReferentUuid;
import net.jini.id.ReferentUuids;
import com.sun.jini.lease.AbstractLease;

/**
 * Basic implementation of <code>net.jini.core.lease.Lease</code> that works
 * with the the Landlord protocol.
 *
 * @author Sun Microsystems, Inc.
 *
 * @see Landlord
 * @since 2.0
 */
public class LandlordLease extends AbstractLease implements ReferentUuid {
    static final long serialVersionUID = 2L;

    /**
     * A universally unique ID that identifies the lease this object
     * represents.
     * @serial 
     */
    final private Uuid cookie;

    /**
     * Owner of the resource associated with this lease.
     * @serial
     */
    final private Landlord landlord;

    /**
     * The <code>Uuid</code> of the <code>landlord</code>. Used
     * to determine if two leases can be batched together.
     * @serial
     */
    final private Uuid landlordUuid;

    /**
     * Create a new <code>LandlordLease</code>.
     * @param cookie a <code>Uuid</code> that universally and uniquely
     *                 identifies the lease this object is to be a proxy for
     * @param landlord <code>Landlord</code> object that will be used to
     *                 communicate renew and cancel requests to the granter
     *                 of the lease
     * @param landlordUuid a universally unique id that has been
     *                 assigned to the server granting of the lease.
     *                 Ideally the <code>Uuid</code> {@link
     *                 ReferentUuid#getReferentUuid landlord.getUuid} would
     *                 return if <code>landlord</code> implemented
     *                 {@link ReferentUuid}. Used to determine when
     *                 two leases can be batched together.
     * @param expiration the initial expiration time of the lease in
     *                 milliseconds since the beginning of the epoch
     * @throws NullPointerException if <code>landlord</code>, 
     *                 <code>landlordUuid</code> or <code>cookie</code>
     *                 is null  
     */
    public LandlordLease(Uuid cookie, Landlord landlord, Uuid landlordUuid,
			 long expiration)
    {
	super(expiration);

	if (cookie == null) 
	    throw new NullPointerException("Can't create a LandlordLease " +
					   "with a null cookie");

	if (landlord == null)
	    throw new NullPointerException("Can't create a LandlordLease " +
					   "with a null landlord");

	if (landlordUuid == null)
	    throw new NullPointerException("Can't create a LandlordLease " +
					   "with a null landlordUuid");

	this.cookie   = cookie;
	this.landlord = landlord;
	this.landlordUuid = landlordUuid;
    }

    // Implementation of the Lease interface

    // purposefully inherit doc comment from supertype
    public void cancel() throws UnknownLeaseException, RemoteException {
	landlord.cancel(cookie);
    }

    // purposefully inherit doc comment from supertype
    protected long doRenew(long renewDuration)
	 throws LeaseDeniedException, UnknownLeaseException, RemoteException
    {
	if (renewDuration < 0 && !(renewDuration == Lease.FOREVER ||
				   renewDuration == Lease.ANY))
	{
	    throw new IllegalArgumentException("Lease renewal: " +
	        "Asked for a negative duration");
	}
	return landlord.renew(cookie, renewDuration);
    }

    // purposefully inherit doc comment from supertype
    public Uuid getReferentUuid() {
	return cookie;
    }

    // purposefully inherit doc comment from supertype
    public boolean equals(Object other) {
	return ReferentUuids.compare(this, other);

	// Note, we do not include the expiration in the equality test.
	// If the lease is copied and ether the copy or the original
	// is renewed they are conceptually the same because they
	// still represent the same claim on the same resource
	// --however their expiration will be different
    }

    // inherit doc comment
    public boolean canBatch(Lease lease) {
	if (lease instanceof LandlordLease) {
  	    return landlordUuid.equals(((LandlordLease)lease).landlordUuid);
	}

       	return false;
    }

    /** Return the landlord. */
    Landlord landlord() {
	return landlord;
    }

    /** Return the landlord's Uuid. */
    Uuid landlordUuid() {
	return landlordUuid;
    }

    /** Return the cookie. */
    Uuid cookie() {
	return cookie;
    }

    /** Set the expiration. */
    void setExpiration(long expiration) {
	this.expiration = expiration;
    }

    // inherit doc comment
    public LeaseMap createLeaseMap(long duration) {
	return new LandlordLeaseMap(landlord, landlordUuid, this, duration);
    }

    // purposefully inherit doc comment from supertype
    public int hashCode() {
	return cookie.hashCode();
    }

    // purposefully inherit doc comment from supertype
    public String toString() {
	return "LandlordLease:" + cookie + " landlord:" + landlord +
	    " landlordUuid:" + landlordUuid + " " + super.toString();
    }

    /** Read this object back validating state.*/
    private void readObject(ObjectInputStream in)
	throws IOException, ClassNotFoundException
    {
	in.defaultReadObject();

	if (cookie == null)
	    throw new InvalidObjectException("null cookie reference");

	if (landlord == null)
	    throw new InvalidObjectException("null landlord reference");

	if (landlordUuid == null)
	    throw new InvalidObjectException("null landlordUuid reference");
    }

    /** 
     * We should always have data in the stream, if this method
     * gets called there is something wrong.
     */
    private void readObjectNoData() throws InvalidObjectException {
	throw new 
	    InvalidObjectException("LandlordLease should always have data");
    }
}
