/*
 * Filename: MultiLeasedResource.java
 *
 * Author: Joe Wang
 *
 * Copyright (c) 2003-2005 
 * The Trustees of Columbia University in the City of New York
 * Columbia University Information Technology
 *
 * This file is part of NINJa.
 *
 * NINJa is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * NINJa is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NINJa; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
package edu.columbia.print.ninja.core.service;

import java.util.*;
import com.sun.jini.lease.landlord.LeasedResource;

/**
 * used in cases where a single resource may have multiple leasees
 * @author Joe Wang <jw168@columbia.edu>
 * @since 1.0
 */
class MultiLeasedResource {
    transient Map cookies = new HashMap();
    transient Map values = new HashMap();
    static transient Timer timer = new Timer();
    String name;
    
    MultiLeasedResource(String name) {this.name = name;}
    
    public String getName() {return name;}
    /**
     * @param value the object to be assigned a lease
     * @return the resource to be given to a LeaseManager
     */
    synchronized LeasedResource acquireResource(Object value) {
        LeasedResource resource = new edu.columbia.print.ninja.core.service.MultiLeasedResource.LeasedEntry();
        cookies.put(resource.getCookie(), resource);
        values.put(resource.getCookie(), value);
        timer.schedule(new CleanUp(resource.getCookie()), 1000);
        return resource;
    }
    
/*    synchronized boolean renewResource(Object cookie, long expiration) {
        LeasedResource rsrc = (LeasedResource )cookies.get(cookie);
        if(rsrc==null) return false;
        if(rsrc instanceof LeasedEntry) {
            rsrc.setExpiration(expiration);
        }
        return true;
    }
*/
    /**
     * this should be modified to take the same object passed to acquireResource.
     * @param cookie The cookie associated with the object to be released
     */
    synchronized boolean releaseResource(Object cookie) {
        LeasedResource rsrc = (LeasedResource )cookies.get(cookie);
        if(rsrc==null) {
            System.out.println("MultiResource: cookie not found: "+cookie);
            return true;
        }
        if(rsrc instanceof LeasedEntry) {
            if(((LeasedEntry )rsrc).isLocked()) return false;
        }
        cookies.remove(cookie);
        values.remove(cookie);
        return true;
    }
    
    public java.util.Iterator values() {
        Vector v = new Vector();
        v.addAll(values.values());
        return v.iterator();
    }
    
    public int size() {
        return values.size();
    }
    
    public java.util.Iterator leasedResources() {
        Vector v = new Vector();
        v.addAll(cookies.values());
        return v.iterator();
    }
    
    /**
     * get the value associate with the cookie
     * @param cookie the cookie from the LeasedResource
     * @return the value mapped to the cookie
     */
    public Object getValue(Object cookie) {
        return values.get(cookie);
    }
    
    /**
     * the the resource associated with the cookie
     * @param cookie the cookie from the LeasedResource
     * @return the resource mapped to the cookie
     */
    public LeasedResource getLeasedResource(Object cookie) {
        return (LeasedResource )cookies.get(cookie);
    }
    
    /**
     * produces a unique LeasedEntry each time.
     */
    static class LeasedEntry implements LeasedResource {
        static volatile long counter;
        Object id;
        long expiration;
        boolean locked;
        
        LeasedEntry() {
            id = new Long(counter++);
            locked = false;
        }
        
        public Object getCookie() { return id; }
        public void setExpiration(long expiration) {
            this.expiration=expiration;
        }
        public long getExpiration() {return expiration;}
        public synchronized boolean isLocked() {return locked;}
        public synchronized void lock() {locked=true;}
        public synchronized void unlock() {locked=false;}
    }

    class CleanUp extends TimerTask {
        Object cookie;
        CleanUp(Object cookie) {this.cookie = cookie;}
        public void run() {
            LeasedEntry rsrc = (LeasedEntry )cookies.get(cookie);
            if(rsrc==null) return;
            long remaining = rsrc.getExpiration()-System.currentTimeMillis();
            if(remaining>0) {
                timer.schedule(new CleanUp(cookie), remaining);
                return;
            }
            if(!releaseResource(cookie)) {
                timer.schedule(new CleanUp(cookie), 5*1000);
                return;
            }
        }
    }
}
