<?php
/**
 * Provides the most important status information about the Resin server.
 *
 * @author Sam
 */

require_once "WEB-INF/php/inc.php";

if (! admin_init()) {
  return;
}

$mbean_server = $g_mbean_server;
$resin = $g_resin;
$server = $g_server;

echo "<div id='summary-tabs'>\n";
echo "<ul>\n";

echo "<li><a href='#overview-tab'>Overview</a></li>\n";
echo "<li><a href='#meter-tab'>Meters</a></li>\n";
echo "<li><a href='#table-tab'>Tables</a></li>\n";
echo "<li><a href='#log-tab'>Logs</a></li>\n";
echo "</ul>";

echo "<div id='overview-tab'>";

print_overview();

echo "<h2>Health"; print_Help("Health"); echo "</h2>\n";

display_health_status();

echo "</div>";

echo "<div id='meter-tab'>\n";

print_graphs();

echo "</div>\n";

echo "<div id='table-tab'>\n";

print_threads();
print_tcp_ports();
print_load_balance();
print_database_pools();

echo "</div>\n";

echo "<div id='log-tab'>";

recent_messages();

echo "</div>";

echo "</div>";

function print_overview()
{
  global $g_mbean_server;
  global $g_server;
  global $resin;

  $server = $g_server;

  $runtime = $g_mbean_server->lookup("java.lang:type=Runtime");
  $os = $g_mbean_server->lookup("java.lang:type=OperatingSystem");
  $row = 0;
?>

<table class="data">
  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The server id used when starting this instance of Resin, the value of '-server'.">
    Server id:</td>
    <td><?= $server->Id ? $server->Id : '""' ?></td>
    
    <td class='item'
        title="The operating system user that is running this instance of Resin.">
      User:
    </td>
    <td><?= $resin->UserName ?></td>
    
    <td class='item'
        title="The ip address of the machine that is running this instance of Resin.">
      Machine:
    </td>
    <td><?= $runtime->Name ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The Resin version.">
      Resin:
    </td>
    <td colspan='5'><?= $resin->Version ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The JDK version.">
      JDK:
    </td>
    <td colspan='5'><?= $runtime->VmName ?> <?= $runtime->VmVersion ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The OS version.">
    OS:</th>
    <td colspan='5'>
      <?= $os->AvailableProcessors ?> cpu,
      <?= $os->Name ?>
      <?= $os->Arch ?>
      <?= $os->Version ?>
    </td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The current lifecycle state">
      State:
    </td>
    <td class="<?= $server->State ?>"><?= $server->State ?></td>

    <td class='item'
        title="The time that this instance was last started or restarted.">
      Uptime:
    </td>
    <?php
      $start_time = $server->StartTime->time / 1000;
      $now = $server->CurrentTime->time / 1000;
      $uptime = $now - $start_time;

      if ($uptime < 12 * 3600)
        echo "<td class='warmup' colspan='3'>";
      else
        echo "<td colspan='3'>";

      echo sprintf("%d days %02d:%02d:%02d",
                   $uptime / (24 * 3600),
                   $uptime / 3600 % 24,
                   $uptime / 60 % 60,
                   $uptime % 60);
      echo " -- " . format_datetime($server->StartTime);
     ?>
   </td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The current free amount of heap memory available for the JVM, in bytes.">
	Free heap:
    </td>
    <td><?= format_memory($server->RuntimeMemoryFree) ?></td>
    
    <td class='item'
        title="The current total amount of heap memory available for the JVM, in bytes.">
      Total heap:
    </td>
    <td colspan='3'><?= format_memory($server->RuntimeMemory) ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The current file descriptors used.">
	File des:
    </td>
    <td><?= $os->OpenFileDescriptorCount ?></td>
    
    <td class='item'
        title="The maximum file descriptors available.">
	File des max:
    </td>
    <td colspan='3'><?= $os->MaxFileDescriptorCount ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The free amount of operating-system swap space.">
	Free Swap:
    </td>
    <td><?= format_memory($os->FreeSwapSpaceSize) ?></td>
    
    <td class='item'
        title="The total amount of operating-system swap space.">Total Swap:</th>
    <td colspan='3'><?= format_memory($os->TotalSwapSpaceSize) ?></td>
  </tr>

  <tr class='<?= row_style($row++) ?>'>
    <td class='item'
        title="The free amount of operating-system physical memory.">Free Physical:</td>
    <td><?= format_memory($os->FreePhysicalMemorySize) ?></td>
    
    <td class='item'
        title="The total amount of operating-system physical memory.">Total Physical:</td>
    <td colspan='3'><?= format_memory($os->TotalPhysicalMemorySize) ?></td>
  </tr>


<?php

  if ($mbean_server) {
    $block_cache = $mbean_server->lookup("resin:type=BlockManager");
    $proxy_cache = $mbean_server->lookup("resin:type=ProxyCache");
  }

  echo "</table>\n";
}

/* graphs */

function print_graphs()
{
  global $g_mbean_server;
  global $g_server;

  $mbean_server = $g_mbean_server;
  $server = $g_server;
  
  $si = sprintf("%02d", $server->ServerIndex);
  $stat_service = $mbean_server->lookup("resin:type=StatService");

  if ($stat_service) {
    echo "<h2>Graphs</h2>\n";

    $period = 6 * 60 * 60;
    $end = time();
    $start = $end - $period;

    $height = 150;
    $width = 240;

    echo "<table>";

    echo "<tr><th>Request Count<th>Request Time<th>Threads";
    echo "<tr valign='top'><td>";
    stat_graph("request", $width, $height, $start, $end,
               array("$si|Resin|Http|Request Count"));
    echo "<td>";
    stat_graph("request_time", $width, $height, $start, $end,
               array("$si|Resin|Http|Request Time",
                     "$si|Resin|Http|Request Time Max"));
    echo "<td>";
    stat_graph("thread", $width, $height, $start, $end,
               array("$si|JVM|Thread|JVM Thread Count",
                     "$si|Resin|Thread|Thread Count",
                     "$si|Resin|Thread|Thread Active Count",
                     "$si|Resin|Thread|Thread Task Queue",
                     "$si|Resin|Thread|Thread Priority Queue",
                     "$si|Resin|Thread|Thread Overflow Count"));

    echo "<tr><th>Database Query Active<th>Database Query Time<th>CPU";
    echo "<tr valign='top'><td>";
    stat_graph("database", $width, $height, $start, $end,
               array("$si|Resin|Database|Connection Active"));

    echo "<td>";
    stat_graph("database_time", $width, $height, $start, $end,
               array("$si|Resin|Database|Query Time",
                     "$si|Resin|Database|Query Time Max"));

    echo "<td>";
    stat_graph("cpu", $width, $height, $start, $end,
               array("$si|OS|CPU|CPU Active"));

    echo "<tr><th>Memory<th>GC Time<th>Netstat";
    echo "<tr valign='top'><td>";
    stat_graph("memory", $width, $height, $start, $end,
               array("$si|JVM|Memory|Heap Memory Used",
                     "$si|JVM|Memory|Heap Memory Free",
                     "$si|JVM|Memory|Tenured Memory Used",
                     "$si|JVM|Memory|PermGen Memory Used"));

    echo "<td>";
    stat_graph_regexp("memory_gc", $width, $height, $start, $end,
                      "/$si\|JVM\|Memory\|GC Time/");
                     
    echo "<td>";
    stat_graph_regexp("netstat", $width, $height, $start, $end,
                      "/$si\|OS\|Network\|(?!tcp-time)/");
                    
    echo "</table>";
  }
}

function recent_messages()
{
  global $mbean_server;

  if ($mbean_server) {
    $mbean = $mbean_server->lookup("resin:type=LogService");
  }

  //
  // recent messages
  //
  
  if ($mbean) {
    $now = time();

    $messages = $mbean->findMessages("warning",
                                     ($now - 24 * 3600) * 1000,
                                     $now * 1000);

    if (! empty($messages)) {
      echo "<h2>Recent Messages</h2>\n";

      echo "<table class='data'>\n";
  /*
      //echo "<thead class='scroll'>\n";
      echo "<tr><th class='date'>Date</th>";
      echo "    <th class='level'>Level</th>";
      echo "    <th class='message'>Message</th></tr>\n";
      //echo "</thead>\n";
  */

      $messages = array_reverse($messages);

      echo "<tbody class='scroll'>\n";
      foreach ($messages as $message) {
        echo "<tr class='{$message->level}'>";
        echo "  <td class='date'>";
        echo strftime("%Y-%m-%d&nbsp;%H:%M:%S", $message->timestamp / 1000);
        echo "</td>";
        echo "  <td class='level'>{$message->level}</td>";
        echo "  <td class='message'>" . htmlspecialchars(wordwrap($message->message, 90));
        echo "  </td>";
        echo "</tr>";
      }

      echo "</tbody>\n";
      echo "</table>\n";
    }

    //
    // startup
    //
    $start_time = $server->StartTime->time / 1000;

    $messages = $mbean->findMessages(($start_time - 15 * 60) * 1000, ($start_time - 2) * 1000);

    if (! empty($messages)) {
      echo "<h2>Shutdown Messages</h2>\n";

      echo "<table class='data'>\n";
/*
    //echo "<thead class='scroll'>\n";
    echo "<tr><th class='date'>Date</th>";
    echo "    <th class='level'>Level</th>";
    echo "    <th class='message'>Message</th></tr>\n";
    //echo "</thead>\n";
*/

      $messages = array_reverse($messages);

      echo "<tbody class='scroll'>\n";

      // mark the start time
      echo "<tr class='warning'>";
      echo "  <td class='date'>";
      echo strftime("%Y-%m-%d %H:%M:%S", $start_time);
      echo "</td>";
      echo "  <td class='level'></td>";
      echo "  <td class='message'>Start Time</td>";
      echo "</tr>";

      foreach ($messages as $message) {
        echo "<tr class='{$message->level}'>";
        echo "  <td class='date'>";
        echo strftime("%Y-%m-%d %H:%M:%S", $message->timestamp / 1000);
        echo "</td>";
        echo "  <td class='level'>{$message->level}</td>";
        echo "  <td class='message'>" . htmlspecialchars(wordwrap($message->message, 90)) . "</td>";
        echo "</tr>";
      }

      echo "</tbody>\n";
      echo "</table>\n";
    }
  }
}

function print_tcp_ports()
{
  global $server;

  $thread_pool = $server->ThreadPool;

  $ports = $server->Ports;

  if ($ports) {
    echo "<h2>TCP ports"; print_help("Ports"); echo "</h2>\n";
?>

<table class="data">
  <tr>
    <th colspan='2'>&nbsp;</th>
  <th colspan='3'>Threads</th>
  <th colspan='4'>Keepalive</th>

  <tr>
    <th>&nbsp;</th>
    <th>Status</th>

    <th>Active</th>
    <th>Idle</th>
    <th>Total</th>

    <th>Total</th>
    <th>Thread</th>
    <th>Non-Block</th>
    <th>Comet</th>
  </tr>
<?php
    $count = 0;
    $row = 0;

    foreach ($server->Ports as $port) {
      $count++;
?>

  <tr class='<?= $row++ % 2 == 0 ? "ra" : "rb" ?>'>
    <td class='item'><?= $port->ProtocolName ?>://<?= $port->Address ? $port->Address : "*" ?>:<?= $port->Port ?></td>
    <td class="<?= $port->State ?>"><?= $port->State ?></td>
    <td><?= $port->ThreadActiveCount ?></td>
    <td><?= $port->ThreadIdleCount ?></td>
    <td><?= $port->ThreadCount ?></td>

    <td><?= $port->KeepaliveCount ?></td>
    <td><?= $port->KeepaliveThreadCount ?></td>
    <td><?= $port->KeepaliveSelectCount ?></td>
    <td><?= $port->CometIdleCount ?>
  </tr>
<?php
    }

    echo "</table>";
  }
}

function print_threads()
{
  global $mbean_server;
  global $server;

  $conn = null;

  $jvm_thread = $mbean_server->lookup("java.lang:type=Threading");

  $connInfoList = array();

  foreach ($server->Ports as $port) {
    $portConnInfo = $port->connectionInfo();

    if ($portConnInfo)
      $connInfoList = array_merge($connInfoList, $portConnInfo);
  }

  $row = 0;
  @ usort($connInfoList, "conn_time_cmp");

  echo "<h3>Open Connections</h3>";

  echo "<table class='data'>";
  echo "<tr><th><th>time<th>id<th>url<th>IP</tr>\n";

  foreach ($connInfoList as $connInfo) {
    if ($connInfo->requestTime >= 0 && $connInfo->threadId >= 0) {
      echo "<tr class='" . ($row++ % 2 == 0 ? "ra" : "rb") . "'>\n";
      echo "<tr>\n";
      echo "<td>\n";

      $pname = "port_" . $row;
    
      $threadId = $connInfo->threadId;
    
      $show = "hide('s_$pname');show('h_$pname');show('h1_$pname')";
      $hide = "show('s_$pname');hide('h_$pname');hide('h1_$pname')";
      echo "<a id='s_$pname' href=\"javascript:$show\">[show]</a> ";
      echo "<a id='h_$pname' href=\"javascript:$hide\" style='display:none'>[hide]</a> ";
      echo "<td>";
    
      echo sprintf("%.3fs", $connInfo->requestTime * 0.001);
      // echo " thread=" . $threadId;
      echo "<td>";
      echo $connInfo->id;
    
      echo "<td>";
      $conn = $server->findConnectionByThreadId($threadId);
      if ($conn)
        echo $conn->getUrl();
      else
        echo $connInfo->state;

      echo "<td>";
      echo " " . $conn->getRemoteAddress();
      echo "\n";
      echo "</tr>";

      echo "<tr><td colspan='5'>";
      echo "<pre id='h1_$pname' style='display:none'>";

      $thread = $jvm_thread->getThreadInfo($threadId, 50);
      /*
      foreach ($thread->stackTrace as $trace) {
        echo "     at " . $trace->className . "." . $trace->methodName . "\n";
      }
      */

      echo $thread;

      echo "</pre>";
      echo "</td>";
      echo "</tr>";
    }
  }

  echo "</table>\n";
}

function print_load_balance()
{
  global $resin;

  echo "<!-- Cluster -->\n";

  echo "<h2>Load Balance and Clusters";
  print_help("Load Balance");
  echo "</h2>\n"

?>

<table class="data">
  <tr>
    <th>Server</th>
    <th>Address</th>
    <th>Status</th>
    <th>Active</th>
    <th>Idle</th>
    <th>Connection Miss</th>
    <th>Load</th>
    <th>Latency</th>
    <th colspan='2'>Failures</th>
    <th colspan='2'>Busy</th>
  </tr>
<?php

  foreach ($resin->Clusters as $cluster) {
    if (empty($cluster->Servers))
      continue;

    echo "<tr><td class='group' colspan='12'>$cluster->Name</td></tr>\n";

  $count = 0;
  foreach ($cluster->Servers as $client) {
?>

  <tr class='<?= $count++ % 2 == 0 ? "ra" : "rb" ?>'>
    <td class='item'><?= $client->Name ?></td>
    <td><?= $client->Address ?>:<?= $client->Port ?></td>
    <td class="<?= $client->State ?>"><?= $client->State ?></td>
    <td><?= $client->ConnectionActiveCount ?></td>
    <td><?= $client->ConnectionIdleCount ?></td>
    <td><?= format_miss_ratio($client->ConnectionKeepaliveCountTotal,
                              $client->ConnectionNewCountTotal) ?></td>
    <td><?= sprintf("%.2f", $client->ServerCpuLoadAvg) ?></td>
    <td><?= sprintf("%.2f", $client->LatencyFactor) ?></td>
    <?php
      format_ago_td_pair($client->ConnectionFailCountTotal,
                         $client->LastFailTime);

      format_ago_td_pair($client->ConnectionBusyCountTotal,
                         $client->LastBusyTime);

    ?>
  </tr>
<?php 
    }
  }

  echo "</table>\n";
}  

function print_database_pools()
{
  global $mbean_server;

  if ($mbean_server) {
    $db_pools = $mbean_server->query("resin:*,type=ConnectionPool");
  }

  if ($db_pools) {
    require_once("WEB-INF/php/db.php");

    echo "<h2>Database Pools";
    print_help("Database Pools");
    echo "</h2>\n"

    echo print_db_pools($db_pools);
  }
}

/*
if ($mbean_server) {
  $store = $mbean_server->lookup("resin:type=PersistentStore");
}

if ($store) {
  echo "<h2>Persistent Store: $store->StoreType</h2>\n";
  echo "<table class='data'>";

  echo "<tr><th>Object Count</th><td>$store->ObjectCount</td>\n";
  echo "<tr><th>Load Count</th><td>$store->LoadCountTotal</td>\n";
  echo "<tr><th>Load Fail Count</th><td>$store->LoadFailCountTotal</td>\n";
  echo "<tr><th>Save Count</th><td>$store->SaveCountTotal</td>\n";
  echo "<tr><th>Save Fail Count</th><td>$store->SaveFailCountTotal</td>\n";
  echo "</table>";
}
*/

function conn_time_cmp($conn_a, $conn_b)
{
  return $conn_b->requestTime - $conn_a->requestTime;
}

function display_health_status()
{
  global $g_mbean_server;

  $health = $g_mbean_server->lookup("resin:type=HealthCheck,name=Resin");
      
  $is_health = $health;

  echo "<table border='0'>";
  echo "<tr><td>";

  echo "<span id='sw_health' class='switch'></span>";

  echo "</td><td>";

  if (! $health) {
    echo "n/a";
  }
  else if ($health->Status == "OK") {
    print_ok("");
    echo " health " . $health->Status;
  }
  else if ($health->Status == "FAIL") {
    print_fail("health " . $health->Status);
  }
  else {
    echo  "health " . $health->Status;
  }

  echo "</td></tr>";

  if ($g_mbean_server && $health) {
    $health_list = $g_mbean_server->query("resin:type=HealthCheck,*");
    foreach ($health_list as $s_health) {
      echo "<tr class='toggle-sw_health'>";
      echo "<td>";
      echo "</td>";
      echo "<td>";

      if ($s_health->Status == "FAIL") {
        print_fail(" " . $s_health->Name);
      }
      else if ($s_health->Status == "OK") {
        print_ok(""); echo " " . $s_health->Name;
      }
      else {
        print_warn(" " . $s_health->Name);
      }

      echo "</td>";
      echo "<td>";
      echo $s_health->Message;
      echo "</td>";
      echo "</tr>";
    }
  }

  echo "</table>";

  display_health_graph();
}

function display_health_graph()
{
  global $mbean_server;
  global $server;

  $si = sprintf("%02d", $server->ServerIndex);
  $stat_service = $mbean_server->lookup("resin:type=StatService");

  if ($stat_service) {
    $period = 24 * 60 * 60;
    $end = time();
    $start = $end - $period;

    $height = 150;
    $width = 400;

    stat_graph_regexp("health", $width, $height, $start, $end,
                      "/^$si\|Resin\|Health/", "right");
  }
}

  display_footer($g_page, $javascript. javascript_create_tab("summary-tabs"));

?>
