/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/network/listen/SocketLinkState.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is finishThread 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.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.network.listen;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.inject.Module;

@Module
enum SocketLinkState {
  /**
   * The allocated, ready to accept state
   */
  INIT {
    @Override
    boolean isAllowIdle() { return true; }
    
    @Override
    SocketLinkState toInit(TcpSocketLink conn) 
    { 
      return INIT; 
    }
    
    @Override
    SocketLinkState toIdle()
    { 
      return IDLE;
    }

    @Override
    SocketLinkState toAccept() 
    { 
      return ACCEPT; 
    }
  },
  
  /**
   * Waiting in an accept() for a new connection
   */
  ACCEPT {               // accepting
    @Override
    boolean isActive() { return true; }

    @Override
    SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) 
    { 
      conn.getListener().keepaliveAllocate();
      
      return REQUEST_ACTIVE_KA;
    }

    @Override
    SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) 
    { 
      return REQUEST_ACTIVE_NKA;
    }
  },

  /**
   * Processing a request with a keepalive slot allocated.
   */
  REQUEST_ACTIVE_KA {       // processing a request
    @Override
    boolean isActive() { return true; }

    @Override
    boolean isRequestActive() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }
    
    @Override
    SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) 
    { 
      return REQUEST_ACTIVE_KA; 
    }

    @Override
    SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) 
    { 
      conn.getListener().keepaliveFree();
      
      return REQUEST_ACTIVE_NKA;
    }

    @Override
    SocketLinkState toKillKeepalive(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return REQUEST_ACTIVE_NKA;
    }

    @Override
    SocketLinkState toKeepalive(TcpSocketLink conn)
    {
      return KEEPALIVE_THREAD;
    }

    @Override
    SocketLinkState toComet()
    {
      return COMET_KA;
    }

    @Override
    SocketLinkState toDuplex(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return DUPLEX;
    }
    
    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return CLOSED;
    }
  },

  /**
   * Request active with keepalive forbidden.
   */
  REQUEST_ACTIVE_NKA {          // processing a request, but keepalive forbidden
    @Override
    boolean isActive() { return true; }

    @Override
    SocketLinkState toComet()
    {
      return COMET_NKA;
    }

    @Override
    SocketLinkState toDuplex(TcpSocketLink conn)
    {
      return DUPLEX;
    }
  },

  /**
   * Waiting for a read from the keepalive connection.
   */
  KEEPALIVE_THREAD {   // waiting for keepalive data
    @Override
    boolean isKeepalive() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }

    @Override
    SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) 
    { 
      return REQUEST_ACTIVE_KA; 
    }

    @Override
    SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) 
    {
      conn.getListener().keepaliveFree();
      
      return REQUEST_ACTIVE_NKA; 
    }

    @Override
    SocketLinkState toKeepaliveSelect()
    {
      return KEEPALIVE_SELECT;
    }

    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();

      return CLOSED;
    }
  },    

  KEEPALIVE_SELECT {   // waiting for keepalive data (select)
    @Override
    boolean isKeepalive() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }

    @Override
    SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) 
    { 
      return REQUEST_ACTIVE_KA; 
    }

    @Override
    SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) 
    {
      conn.getListener().keepaliveFree();

      return REQUEST_ACTIVE_NKA; 
    }

    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();

      return CLOSED;
    }
  },    

  /**
   * Comet request with a keepalive allocated.
   */
  COMET_KA {                // processing an active comet service
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometActive() { return true; }

    @Override
    boolean isAsyncStarted() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }

    @Override
    SocketLinkState toCometSuspend(TcpSocketLink conn)
    {
      conn.getListener().cometSuspend(conn);
      
      return COMET_SUSPEND_KA;
    }
    

    @Override
    SocketLinkState toCometComplete()
    {
      return COMET_COMPLETE_KA;
    }

    @Override
    SocketLinkState toKillKeepalive(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return COMET_NKA;
    }
    
    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return CLOSED;
    }
  },

  COMET_NKA {            // processing an active comet service
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometActive() { return true; }

    @Override
    boolean isAsyncStarted() { return true; }

    @Override
    SocketLinkState toCometSuspend(TcpSocketLink conn)
    {
      conn.getListener().cometSuspend(conn);
      
      return COMET_SUSPEND_NKA;
    }

    @Override
    SocketLinkState toCometComplete()
    {
      return COMET_COMPLETE_NKA;
    }
  },

  COMET_SUSPEND_KA {        // suspended waiting for a wake
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometSuspend() { return true; }

    @Override
    boolean isAsyncStarted() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }

    @Override
    SocketLinkState toKillKeepalive(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return COMET_SUSPEND_NKA;
    }

    @Override
    SocketLinkState toCometResume(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      return REQUEST_ACTIVE_KA;
    }
        
    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      throw new IllegalStateException(this + " " + conn);
    }

    @Override
    SocketLinkState toDestroy(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      throw new IllegalStateException(this + " " + conn);
    }
  },

  COMET_SUSPEND_NKA {    // suspended waiting for a wake
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometSuspend() { return true; }

    @Override
    boolean isAsyncStarted() { return true; }

    @Override
    SocketLinkState toCometResume(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      return REQUEST_ACTIVE_NKA;
    }
    
    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      throw new IllegalStateException(this + " " + conn);
    }

    @Override
    SocketLinkState toDestroy(TcpSocketLink conn)
    {
      conn.getListener().cometDetach(conn);
      
      throw new IllegalStateException(this + " " + conn);
    }
  },

  COMET_COMPLETE_KA {       // complete or timeout
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometComplete() { return true; }

    @Override
    boolean isKeepaliveAllocated() { return true; }

    @Override
    SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) 
    { 
      return REQUEST_ACTIVE_KA;
    }

    @Override
    SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) 
    { 
      conn.getListener().keepaliveFree();
      
      return REQUEST_ACTIVE_NKA;
    }

    @Override
    SocketLinkState toKillKeepalive(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return COMET_COMPLETE_NKA;
    }

    @Override
    SocketLinkState toCometComplete()
    {
      return this;
    }

    @Override
    SocketLinkState toKeepalive(TcpSocketLink conn)
    {
      return KEEPALIVE_THREAD;
    }
    
    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().keepaliveFree();
      
      return CLOSED;
    }
  },

  COMET_COMPLETE_NKA {   // complete or timeout
    @Override
    boolean isComet() { return true; }

    @Override
    boolean isCometComplete() { return true; }

    @Override
    SocketLinkState toCometComplete()
    {
      return this;
    }
  },

  DUPLEX {               // converted to a duplex/websocket
    @Override
    boolean isDuplex() { return true; }

    @Override
    SocketLinkState toKeepalive(TcpSocketLink conn)
    {
      conn.getListener().duplexKeepaliveBegin();

      return DUPLEX_KEEPALIVE;
    }

    @Override
    SocketLinkState toDuplexActive(TcpSocketLink conn)
    {
      return DUPLEX;
    }
  },

  DUPLEX_KEEPALIVE {     // waiting for duplex read data
    @Override
    boolean isDuplex() { return true; }

    @Override
    boolean isKeepalive() { return true; }

    @Override
    SocketLinkState toKeepaliveSelect()
    {
      return DUPLEX_KEEPALIVE;
    }

    @Override
    SocketLinkState toDuplexActive(TcpSocketLink conn)
    {
      conn.getListener().duplexKeepaliveEnd();

      return DUPLEX;
    }

    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      conn.getListener().duplexKeepaliveEnd();

      return CLOSED;
    }
  },

  CLOSED {               // connection closed, ready for accept
    @Override
    boolean isClosed() { return true; }

    @Override
    boolean isAllowIdle() { return true; }

    @Override
    SocketLinkState toAccept() 
    { 
      return ACCEPT; 
    }

    @Override
    SocketLinkState toIdle()
    {
      return IDLE;
    }
  },

  IDLE {                // TcpConnection in free list 
    @Override
    boolean isIdle() { return true; }

    @Override
    SocketLinkState toInit(TcpSocketLink conn) 
    { 
      return INIT; 
    }

    @Override
    SocketLinkState toAccept()
    { 
      return ACCEPT;
    }
    
    @Override
    SocketLinkState toFree(TcpSocketLink conn)
    {
      return this;
    }

    @Override
    SocketLinkState toDestroy(TcpSocketLink conn)
    {
      throw new IllegalStateException(this + " is an illegal destroy state");
    }
  },                    

  DESTROYED {            // connection destroyed
    @Override
    boolean isClosed() { return true; }

    @Override
    boolean isDestroyed() { return true; }

    @Override
    SocketLinkState toClosed(TcpSocketLink conn)
    {
      return this;
    }

    @Override
    SocketLinkState toDestroy(TcpSocketLink conn)
    {
      return this;
    }
  };
  
  // fields
  
  private static final Logger log
    = Logger.getLogger(SocketLinkState.class.getName());

  //
  // predicates
  //

  boolean isIdle()
  {
    return false;
  }
  
  boolean isComet()
  {
    return false;
  }

  boolean isCometActive()
  {
    return false;
  }

  boolean isCometSuspend()
  {
    return false;
  }

  boolean isCometWake()
  {
    return false;
  }
  
  boolean isAsyncStarted()
  {
    return false;
  }

  boolean isCometComplete()
  {
    return false;
  }

  boolean isDuplex()
  {
    return false;
  }
  
  /**
   * True if a keepalive has been allocated, i.e. if the connection
   * is allowed to keepalive to the next request.
   */
  boolean isKeepaliveAllocated()
  {
    return false;
  }

  /**
   * True if the state is one of the keepalive states, either
   * a true keepalive-select or duplex.
   */
  boolean isKeepalive()
  {
    return false;
  }

  boolean isActive()
  {
    return false;
  }

  boolean isRequestActive()
  {
    return false;
  }

  boolean isClosed()
  {
    return false;
  }

  boolean isDestroyed()
  {
    return false;
  }

  boolean isAllowIdle()
  { 
    return false; 
  }

  //
  // state changes
  //

  /**
   * Convert from the idle (pooled) or closed state to the initial state
   * before accepting a connection.
   */
  SocketLinkState toInit(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to init for " + conn);
  }

  /**
   * Change to the accept state.
   */
  SocketLinkState toAccept()
  {
    throw new IllegalStateException(this + " cannot switch to accept");
  }

  /**
   * Changes to the active state.
   */
  SocketLinkState toActive(TcpSocketLink conn, long connectionStartTime)
  {
    if (conn.getListener().isKeepaliveAllowed(connectionStartTime))
      return toActiveWithKeepalive(conn);
    else {
      if (log.isLoggable(Level.FINE))
        log.fine(conn + " keepalive disallowed");
      
      return toActiveNoKeepalive(conn);
    }
  }
  /**
   * Changes to the active state with the keepalive allocated.
   */
  SocketLinkState toActiveWithKeepalive(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to active");
  }

  /**
   * Changes to the active state with no keepalive allocatedn.
   */
  SocketLinkState toActiveNoKeepalive(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to active");
  }

  /**
   * Kill the keepalive, i.e. remove the keepalive allocation.
   */
  SocketLinkState toKillKeepalive(TcpSocketLink conn)
  {
    return this;
  }

  SocketLinkState toKeepalive(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to keepalive");
  }

  SocketLinkState toKeepaliveSelect()
  {
    throw new IllegalStateException(this + " cannot switch to keepalive select");
  }

  //
  // comet
  //

  SocketLinkState toComet()
  {
    throw new IllegalStateException(this + " cannot switch to comet");
  }

  SocketLinkState toCometSuspend(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot suspend comet");
  }

  /*
  SocketLinkState toCometResume()
  {
    throw new IllegalStateException(this + " cannot resume comet");
  }
  */

  SocketLinkState toCometResume(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot resume comet");
  }
  
  SocketLinkState toCometDispatch()
  {
    throw new IllegalStateException(this + " cannot dispatch comet");
  }
    
  SocketLinkState toCometComplete()
  {
    throw new IllegalStateException(this + " cannot complete comet");
  }

  //
  // duplex/websocket
  //

  SocketLinkState toDuplex(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to duplex/websocket");
  }

  SocketLinkState toDuplexActive(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " cannot switch to duplex/websocket");
  }

  //
  // idle/close
  //

  SocketLinkState toIdle()
  {
    throw new IllegalStateException(this + " is an illegal idle state");
  }
  
  SocketLinkState toFree(TcpSocketLink conn)
  {
    throw new IllegalStateException(this + " is an illegal free state for " + conn);
  }

  SocketLinkState toClosed(TcpSocketLink conn)
  {
    return CLOSED;
  }

  SocketLinkState toDestroy(TcpSocketLink conn)
  {
    toClosed(conn);

    conn.getListener().closeConnection(conn);

    return DESTROYED;
  }
}
