/*
 * @(#)CorbaConnectionCacheBase.java	1.30 10/03/23
 * 
 * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.transport;

import java.util.Collection;
import java.util.Iterator;

import com.sun.corba.se.pept.broker.Broker;
import com.sun.corba.se.pept.transport.Connection;
import com.sun.corba.se.pept.transport.ConnectionCache;

import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.transport.CorbaConnectionCache;

import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.orbutil.ORBUtility;

/**
 * @author Harold Carr
 */
public abstract class CorbaConnectionCacheBase
    implements
	ConnectionCache,
	CorbaConnectionCache
{
    protected ORB orb;
    protected long timestamp = 0;
    protected String cacheType;
    protected String monitoringName;
    protected ORBUtilSystemException wrapper;

    protected CorbaConnectionCacheBase(ORB orb, String cacheType,
				       String monitoringName)
    {
	this.orb = orb;
	this.cacheType = cacheType;
	this.monitoringName = monitoringName;
	wrapper =ORBUtilSystemException.get(orb,CORBALogDomains.RPC_TRANSPORT);
	registerWithMonitoring();
	dprintCreation();
    }
    
    ////////////////////////////////////////////////////
    //
    // pept.transport.ConnectionCache
    //
    
    public String getCacheType()
    {
	return cacheType;
    }

    public synchronized void stampTime(Connection c)
    {
	// _REVISIT_ Need to worry about wrap around some day
        c.setTimeStamp(timestamp++);
    }

    public long numberOfConnections()
    {
	synchronized (backingStore()) {
	    return values().size();
	}
    }

    public void close() {
         synchronized (backingStore()) {
             for (Object obj : values()) {
                 ((CorbaConnection)obj).closeConnectionResources() ;
             }
       }
    }

    public long numberOfIdleConnections()
    {
	long count = 0;
	synchronized (backingStore()) {
	    Iterator connections = values().iterator();
	    while (connections.hasNext()) {
		if (! ((Connection)connections.next()).isBusy()) {
		    count++;
		}
	    }
	}
	return count;
    }

    public long numberOfBusyConnections()
    {
	long count = 0;
	synchronized (backingStore()) {
	    Iterator connections = values().iterator();
	    while (connections.hasNext()) {
		if (((Connection)connections.next()).isBusy()) {
		    count++;
		}
	    }
	}
	return count;
    }

    /**
     * Discarding least recently used Connections that are not busy
     *
     * This method must be synchronized since one WorkerThread could
     * be reclaming connections inside the synchronized backingStore
     * block and a second WorkerThread (or a SelectorThread) could have
     * already executed the if (numberOfConnections <= .... ). As a
     * result the second thread would also attempt to reclaim connections.
     *
     * If connection reclamation becomes a performance issue, the connection
     * reclamation could make its own task and consequently executed in
     * a separate thread.
     * Currently, the accept & reclaim are done in the same thread, WorkerThread
     * by default. It could be changed such that the SelectorThread would do
     * it for SocketChannels and WorkerThreads for Sockets by updating the
     * ParserTable.
     */
    synchronized public boolean reclaim()
    {
	try {
	    long numberOfConnections = numberOfConnections();

	    if (orb.transportDebugFlag) {
		dprint(".reclaim->: " + numberOfConnections
			+ " ("
			+ orb.getORBData().getHighWaterMark()
			+ "/"
			+ orb.getORBData().getLowWaterMark()
			+ "/"
			+ orb.getORBData().getNumberToReclaim()
			+ ")");
	    }

	    if (numberOfConnections <= orb.getORBData().getHighWaterMark() ||
		numberOfConnections < orb.getORBData().getLowWaterMark()) {
		return false;
	    }
	    
	    Object backingStore = backingStore();
	    synchronized (backingStore) {

	         // REVISIT - A less expensive alternative connection reclaiming 
	         //           algorithm could be investigated.

		for (int i=0; i < orb.getORBData().getNumberToReclaim(); i++) {
		    Connection toClose = null;
		    long lru = java.lang.Long.MAX_VALUE;
		    Iterator iterator = values().iterator();
		    
		    // Find least recently used and not busy connection in cache
		    while ( iterator.hasNext() ) {
			Connection c = (Connection) iterator.next();
			if ( !c.isBusy() && c.getTimeStamp() < lru ) {
			    toClose = c; 
			    lru = c.getTimeStamp();
			}
		    }
		    
		    if ( toClose == null ) {
			return false;
		    }
		    
		    try {
			if (orb.transportDebugFlag) {
			    dprint(".reclaim: closing: " + toClose);
			}
			toClose.close();
		    } catch (Exception ex) {
			// REVISIT - log
		    }
		}

		if (orb.transportDebugFlag) {
		    dprint(".reclaim: connections reclaimed (" 
			    + (numberOfConnections - numberOfConnections()) + ")");
		}
	    }

	    // XXX is necessary to do a GC to reclaim
	    // closed network connections ??
	    // java.lang.System.gc();

	    return true;
	} finally {
	    if (orb.transportDebugFlag) {
		dprint(".reclaim<-: " + numberOfConnections());
	    }
	}
    }

    ////////////////////////////////////////////////////
    //
    // spi.transport.ConnectionCache
    //

    public String getMonitoringName()
    {
	return monitoringName;
    }

    ////////////////////////////////////////////////////
    //
    // Implementation
    //

    // This is public so folb.Server test can access it.
    public abstract Collection values();

    protected abstract Object backingStore();

    protected abstract void registerWithMonitoring();

    protected void dprintCreation()
    {
	if (orb.transportDebugFlag) {
	    dprint(".constructor: cacheType: " + getCacheType()
		   + " monitoringName: " + getMonitoringName());
	}
    }

    protected void dprintStatistics()
    {
	if (orb.transportDebugFlag) {
	    dprint(".stats: "
		   + numberOfConnections() + "/total "
		   + numberOfBusyConnections() + "/busy "
		   + numberOfIdleConnections() + "/idle"
		   + " (" 
		   + orb.getORBData().getHighWaterMark() + "/"
		   + orb.getORBData().getLowWaterMark() + "/"
		   + orb.getORBData().getNumberToReclaim() 
		   + ")");
	}
    }

    protected void dprint(String msg)
    {
	ORBUtility.dprint("CorbaConnectionCacheBase", msg);
    }
}

// End of file.
