/*
 * @(#)ApplicationPoolLogger.java
 *
 * Copyright 2001-2002 by Oracle Corporation,
 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Oracle Corporation.
 */

package oracle.jbo.common.ampool;

import com.sun.java.util.collections.Iterator;
import java.io.PrintWriter;
import java.util.Date;
import oracle.jbo.pool.ResourcePoolLogger;

/**
 * Responsible for logging, collecting, and reporting application pool
 * statistics.
 */
public class ApplicationPoolLogger extends ResourcePoolLogger implements ApplicationPoolListener
{
   // Transactional state statistic
   protected long mNumOfStateActivations                 = 0;
   protected long mNumOfStatePassivations                = 0;

   // Pool events
   protected long mNumOfReferencedInstancesRecycled      = 0;
   protected long mNumOfUnreferencedInstancesRecycled    = 0;
   protected long mNumOfInstancesReused                  = 0;

   // Session statistics
   protected long mAvgNumOfSessionsReferencingState      = 0;
   protected long mAvgNumOfSessionsReferencingStateAccum = 0;

   private long mAvgCounter                              = 0;

   private final ApplicationPoolImpl mPool;

   public ApplicationPoolLogger(ApplicationPoolImpl pool)
   {
      super(pool);
      mPool = pool;
   }

   public void handleEvent(byte eventType)
   {
      // Synchronized on the pool lock.  This is a little weird, but is
      // probably the safest thing to do in order to prevent deadlock from
      // invoking pool methods in the handler logic.
      synchronized(mPool.getSyncLock())
      {
         super.handleEvent(eventType);

         if (eventType == STATE_ACTIVATED)
         {
            mNumOfStateActivations++;
         }
         else if (eventType == STATE_PASSIVATED)
         {
            mNumOfStatePassivations++;
         }
         else if (eventType == REFERENCED_INSTANCE_RECYCLED)
         {
            mNumOfReferencedInstancesRecycled++;
         }
         else if (eventType == UNREFERENCED_INSTANCE_RECYCLED)
         {
            mNumOfUnreferencedInstancesRecycled++;
         }
         else if (eventType == INSTANCE_REUSED)
         {
            mNumOfInstancesReused++;
         }
      }
   }

   public void dumpPoolStatistics(PrintWriter pw)
   {
      int resourceCount = -1;
      int availableResourceCount = -1;
      Iterator instanceInfos = null;
      Iterator sessionCookieInfos = null;
      
      int numOfSessions = -1;
      synchronized(mPool.getSyncLock())
      {
         calculateAverages(true);

         resourceCount = mPool.getResourceCount();         
         availableResourceCount = mPool.getAvailableResourceCount();
         instanceInfos = mPool.getInstanceInfos().values().iterator();
         numOfSessions = mPool.getSessionCookieInfos().size();
         sessionCookieInfos = mPool.getSessionCookieInfos().values().iterator();

         
      }

      // Collect the pool statistics inside a lock?  Why?  It doesn't
      // really matter that they are precise at this moment in time.
      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_INSTANCE_LIFETIME_STATS, AMPoolMessageBundle.class, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INSTANCE_CREATIONS, AMPoolMessageBundle.class, null, mNumOfInstanceCreations, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INSTANCE_REMOVALS, AMPoolMessageBundle.class, null, mNumOfInstanceRemovals, pw);

//      pw.println();
      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_STATE_MANAGEMENT_STATS, AMPoolMessageBundle.class, pw);

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_ACTIVATIONS, AMPoolMessageBundle.class, null, mNumOfStateActivations, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_PASSIVATIONS, AMPoolMessageBundle.class, null, mNumOfStatePassivations, pw);

//      pw.println();
      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_USE_STATS, AMPoolMessageBundle.class, pw);

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_CHECK_OUTS, AMPoolMessageBundle.class, null, mNumOfCheckouts, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_CHECK_INS, AMPoolMessageBundle.class, null, mNumOfCheckins, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_REUSES, AMPoolMessageBundle.class, null, mNumOfInstancesReused, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_REF_INSTANCES_RECYCLED, AMPoolMessageBundle.class, null, mNumOfReferencedInstancesRecycled, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNREF_INSTANCES_RECYCLED, AMPoolMessageBundle.class, null, mNumOfUnreferencedInstancesRecycled, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_CHECK_OUT_FAILURES, AMPoolMessageBundle.class, null, mNumOfCheckoutFailures, pw);

//      pw.println();
      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_INSTANCE_STATS, AMPoolMessageBundle.class, pw);

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INSTANCES, AMPoolMessageBundle.class, null, resourceCount, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_MAX_NUM_OF_INSTANCES, AMPoolMessageBundle.class, null, mMaxNumOfInstances, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_AVG_NUM_OF_INSTANCES, AMPoolMessageBundle.class, null, mAvgNumOfInstances, pw);

//      pw.println();

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_AVAIL_INSTANCES, AMPoolMessageBundle.class, null, availableResourceCount, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_AVG_NUM_OF_AVAIL_INSTANCES, AMPoolMessageBundle.class, null, mAvgNumOfAvailableInstances, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_AVG_NUM_OF_UNAVAIL_INSTANCES, AMPoolMessageBundle.class, null, mAvgNumOfUnavailableInstances, pw);

//      pw.println();

      long referencedApplicationModules = 0;

      long current = System.currentTimeMillis();
      Date age1 = new Date(current - 60000);
      Date age2 = new Date(current - 300000);
      Date age3 = new Date(current - 600000);

      long referencedBucket1 = 0;
      long referencedBucket2 = 0;
      long referencedBucket3 = 0;
      long referencedBucket4 = 0;
      long bucket1 = 0;
      long bucket2 = 0;
      long bucket3 = 0;
      long bucket4 = 0;
      while (instanceInfos.hasNext())
      {
         ApplicationPoolImpl.ApplicationModuleInfo info = (ApplicationPoolImpl.ApplicationModuleInfo)instanceInfos.next();

         if (info.isInList())
         {
            referencedApplicationModules++;
            if (info.getLastUpdate().before(age3))
            {
               referencedBucket4++;
            }
            else if (info.getLastUpdate().before(age2))
            {
               referencedBucket3++;
            }
            else if (info.getLastUpdate().before(age1))
            {
               referencedBucket2++;
            }
            else
            {
               referencedBucket1++;
            }
         }
         else
         {
            if (info.getLastUpdate().before(age3))
            {
               bucket4++;
            }
            else if (info.getLastUpdate().before(age2))
            {
               bucket3++;
            }
            else if (info.getLastUpdate().before(age1))
            {
               bucket2++;
            }
            else
            {
               bucket1++;
            }
         }
      }

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_REF_INSTANCES, AMPoolMessageBundle.class, null, referencedApplicationModules, pw);

//      pw.println();

      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_INSTANCE_AGE_STATS, AMPoolMessageBundle.class, pw);

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_REF_INSTANCES, AMPoolMessageBundle.class, new String[]{"10"}, referencedBucket4, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_REF_INSTANCES, AMPoolMessageBundle.class, new String[]{"5"}, referencedBucket3, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_REF_INSTANCES, AMPoolMessageBundle.class, new String[]{"1"}, referencedBucket2, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_USED_REF_INSTANCES, AMPoolMessageBundle.class, new String[]{"1"}, referencedBucket1, pw);

//      pw.println();

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_INSTANCES, AMPoolMessageBundle.class, new String[]{"10"}, bucket4, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_INSTANCES, AMPoolMessageBundle.class, new String[]{"5"}, bucket3, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_UNUSED_INSTANCES, AMPoolMessageBundle.class, new String[]{"1"}, bucket2, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_USED_INSTANCES, AMPoolMessageBundle.class, new String[]{"1"}, bucket1, pw);

//      pw.println();

      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_SESSION_STATS, AMPoolMessageBundle.class, pw);

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_SESSIONS, AMPoolMessageBundle.class, null, numOfSessions, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_AVG_NUM_OF_SESSIONS_REF_STATE, AMPoolMessageBundle.class, null, mAvgNumOfSessionsReferencingState, pw);

//      pw.println();

      printHeaderLine(AMPoolMessageBundle.MSG_AMPOOL_SESSION_AGE_STATS, AMPoolMessageBundle.class, pw);

      bucket1 = 0;
      bucket2 = 0;
      bucket3 = 0;
      bucket4 = 0;
      while (sessionCookieInfos.hasNext())
      {
         ApplicationPoolImpl.SessionCookieInfo info = (ApplicationPoolImpl.SessionCookieInfo)sessionCookieInfos.next();

         if (info.mLastUpdate.before(age3))
         {
            bucket4++;
         }
         else if (info.mLastUpdate.before(age2))
         {
            bucket3++;
         }
         else if (info.mLastUpdate.before(age1))
         {
            bucket2++;
         }
         else
         {
            bucket1++;
         }
      }

      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INACTIVE_SESSIONS, AMPoolMessageBundle.class, new String[]{"10"}, bucket4, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INACTIVE_SESSIONS, AMPoolMessageBundle.class, new String[]{"5"}, bucket3, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_INACTIVE_SESSIONS, AMPoolMessageBundle.class, new String[]{"1"}, bucket2, pw);
      printStatisticLine(AMPoolMessageBundle.MSG_AMPOOL_NUM_OF_ACTIVE_SESSIONS, AMPoolMessageBundle.class, new String[]{"1"}, bucket1, pw);

//      pw.println();
   }

   protected void calculateAverages(boolean force)
   {
      super.calculateAverages(force);

      // Only do it for every hundred pool checkins/checkouts.  An event driven
      // method was selected instead of a time driven method for two reasons:
      //
      // 1.  The additional thread required for a time driven collection would
      // be intrusive.
      // 2.  The time driven method would not produce reproducible results
      // require for testing.
      //
      // All averages are rounded to floor
      if (force || ((mNumOfCheckouts + mNumOfCheckins) % 100) == 0)
      {
         float numOfSessionsReferencingState = 0;
         Iterator iter = mPool.getSessionCookieInfos().values().iterator();
         while (iter.hasNext())
         {
            ApplicationPoolImpl.SessionCookieInfo info = (ApplicationPoolImpl.SessionCookieInfo)iter.next();
            if (info.mIsReferencingState)
            {
               numOfSessionsReferencingState++;
            }
         }

         mAvgCounter++;
         mAvgNumOfSessionsReferencingState =
            (mAvgNumOfSessionsReferencingStateAccum += numOfSessionsReferencingState)
            / mAvgCounter;
      }
   }
}
