/*
 * 
 * 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.jeri.internal.http;

import com.sun.jini.thread.Executor;
import com.sun.jini.thread.GetThreadPoolAction;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import net.jini.io.context.AcknowledgmentSource;

/**
 * Class for managing server-side functions shared among multiple connections,
 * such as acknowledgment notification.
 *
 * @author Sun Microsystems, Inc.
 * 
 */
public class HttpServerManager {

    private static final Executor userThreadPool = (Executor)
	java.security.AccessController.doPrivileged(
	    new GetThreadPoolAction(true));

    private final AckListenerMap ackListeners;
    private final Object cookieLock = new Object();
    private long nextCookie = new Random().nextLong();

    /**
     * Creates new HttpServerManager which invalidates transport
     * acknowledgments after the given timeout.
     */
    public HttpServerManager(long ackTimeout) {
	ackListeners = new AckListenerMap(ackTimeout);
    }

    /**
     * Returns unique cookie string.
     */
    String newCookie() {
	synchronized (cookieLock) {
	    return Long.toString(Math.abs(nextCookie++), 16);
	}
    }

    /**
     * Registers listener waiting for given cookie.
     */
    void addAckListener(String cookie,
			AcknowledgmentSource.Listener listener)
    {
	if (cookie == null || listener == null) {
	    throw new NullPointerException();
	}
	synchronized (ackListeners) {
	    LinkedList list = (LinkedList) ackListeners.get(cookie);
	    if (list == null) {
		list = new LinkedList();
		ackListeners.put(cookie, list);
	    }
	    list.add(listener);
	}
    }

    /**
     * Notifies all listeners waiting for given cookie with received == true.
     * Notifications are performed in a separate thread.
     */
    void notifyAckListeners(String cookie) {
	if (cookie == null) {
	    throw new NullPointerException();
	}
	final LinkedList list = (LinkedList) ackListeners.remove(cookie);
	if (list != null) {
	    userThreadPool.execute(new Runnable() {
		public void run() { doAckNotifications(list, true); }
	    }, "Ack notifier");
	}
    }

    /**
     * Notifies list of AcknowledgmentSource.Listeners.
     */
    private static void doAckNotifications(LinkedList list, boolean recvd) {
	for (Iterator i = list.iterator(); i.hasNext(); ) {
	    AcknowledgmentSource.Listener al =
		(AcknowledgmentSource.Listener) i.next();
	    try { al.acknowledgmentReceived(recvd); } catch (Throwable th) {}
	}
    }
    
    /**
     * Map for tracking registered AcknowledgmentSource.Listeners.
     */
    private static class AckListenerMap extends TimedMap {
	
	AckListenerMap(long timeout) {
	    super(userThreadPool, timeout);
	}
	
	void evicted(Object key, Object value) {
	    doAckNotifications((LinkedList) value, false);
	}
    }
}
