/******************************************************************************
 * Product: Adempiere ERP & CRM Smart Business Solution                       *
 * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms version 2 of the GNU General Public License as published   *
 * by the Free Software Foundation. This program 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.           *
 * See the GNU General Public License for more details.                       *
 * You should have received a copy of the GNU General Public License along    *
 * with this program; if not, write to the Free Software Foundation, Inc.,    *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.                     *
 * For the text or an alternative of this public license, you may reach us    *
 * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA        *
 * or via info@compiere.org or http://www.compiere.org/license.html           *
 *****************************************************************************/
package org.compiere.web;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.adempiere.util.LogAuthFailure;
import org.apache.ecs.HtmlColor;
import org.apache.ecs.xhtml.a;
import org.apache.ecs.xhtml.b;
import org.apache.ecs.xhtml.body;
import org.apache.ecs.xhtml.br;
import org.apache.ecs.xhtml.font;
import org.apache.ecs.xhtml.form;
import org.apache.ecs.xhtml.h2;
import org.apache.ecs.xhtml.hr;
import org.apache.ecs.xhtml.input;
import org.apache.ecs.xhtml.label;
import org.apache.ecs.xhtml.option;
import org.apache.ecs.xhtml.p;
import org.apache.ecs.xhtml.select;
import org.apache.ecs.xhtml.strong;
import org.apache.ecs.xhtml.table;
import org.apache.ecs.xhtml.td;
import org.apache.ecs.xhtml.th;
import org.apache.ecs.xhtml.tr;
import org.compiere.Adempiere;
import org.compiere.db.AdempiereDatabase;
import org.compiere.db.CConnection;
import org.compiere.model.AdempiereProcessorLog;
import org.compiere.model.MClient;
import org.compiere.model.MSession;
import org.compiere.model.MStore;
import org.compiere.model.MSysConfig;
import org.compiere.model.MSystem;
import org.compiere.model.Query;
import org.compiere.server.AdempiereServerGroup;
import org.compiere.server.AdempiereServerMgr;
import org.compiere.server.AdempiereServerMgr.ServerWrapper;
import org.compiere.util.CLogFile;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger;
import org.compiere.util.CMemoryUsage;
import org.compiere.util.CacheMgt;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Ini;
import org.compiere.util.TimeUtil;
import org.compiere.util.Trx;
import org.compiere.util.WebDoc;
import org.compiere.util.WebEnv;
import org.compiere.util.WebUtil;

/**
 *	Adempiere Server Monitor
 *	
 *  @author Jorg Janke
 *  @version $Id: AdempiereMonitor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $
 */
public class AdempiereMonitor extends HttpServlet
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -7455613826465213838L;

	/**	Logger				*/
	private static CLogger	log = CLogger.getCLogger(AdempiereMonitor.class);
	/**	The Server			*/
	private static AdempiereServerMgr	m_serverMgr = null;
	/** Message				*/
	private static p				m_message = null;
	
	private volatile static ArrayList<File>	m_dirAccessList = null;
	
	/**
	 * 	Get
	 *	@param request request
	 *	@param response response
	 *	@throws javax.servlet.ServletException
	 *	@throws java.io.IOException
	 */
	protected void doGet (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		boolean xmlOutput = false;
		String responseType = request.getParameter("responseContentType");
		xmlOutput = "xml".equalsIgnoreCase(responseType);
			
		m_message = null;
		if (processLogParameter (request, response))
		{
			if (xmlOutput)
				createXMLSummaryPage(request, response);
			return;
		}
		if (processTraceParameter (request, response))
		{
			if (xmlOutput)
				createXMLSummaryPage(request, response);
			return;
		}
		if (processEMailParameter (request, response))
		{
			if (xmlOutput)
				createXMLSummaryPage(request, response);
			return;
		}
		if (processCacheParameter (request, response))
		{
			if (xmlOutput)
				createXMLSummaryPage(request, response);
			return;
		}
		//
		if (processRunNowParameter (request))
			;
		else
			processActionParameter (request,response);
		
		if (xmlOutput)
			createXMLSummaryPage(request, response);
		else
			createSummaryPage(request, response,false);
	}	//	doGet
	
	/**
	 * 	Post
	 *	@param request request
	 *	@param response response
	 *	@throws ServletException
	 *	@throws IOException
	 */
	protected void doPost (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		doGet(request, response);
	}	//	doPost

	/**
	 * 	Process Log Parameter and return log page
	 *	@param request request
	 *	@param response response
	 *	@return true if it was a log request
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private boolean processLogParameter (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		String serverID = WebUtil.getParameter (request, "Log");
		if (serverID == null || serverID.length() == 0)
			return false;
		
		if (log.isLoggable(Level.INFO)) log.info ("ServerID=" + serverID);
		ServerWrapper server = m_serverMgr.getServer(serverID);
		if (server == null || server.getServer() == null)
		{
			m_message = new p();
			m_message.addElement(new strong("Server not found: "));
			m_message.addElement(serverID);
			return false;
		}
		
		WebDoc doc = WebDoc.create ("iDempiere Server Monitor Log");
		//	Body
		body b = doc.getBody();
		//
		p para = new p();
		a link = new a ("idempiereMonitor#" + serverID, "Return");
		para.addElement(link);
		b.addElement(para);
		//
		b.addElement(new h2(server.getServer().getName()));
		//
		table table = new table();
		table.setBorder(1);
		table.setCellSpacing(2);
		table.setCellPadding(2);
		
		//	Header
		tr line = new tr();
		line.addElement(new th().addElement("Created"));
		line.addElement(new th().addElement("Summary"));
	//	line.addElement(new th().addElement("Error"));
		line.addElement(new th().addElement("Reference"));
		line.addElement(new th().addElement("TextMsg"));
	//	line.addElement(new th().addElement("Description"));
		table.addElement(line);
		
		AdempiereProcessorLog[] logs = server.getServer().getLogs();
		for (int i = 0; i < logs.length; i++)
		{
			AdempiereProcessorLog pLog = logs[i];
			line = new tr();
			line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getCreated())));
			line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getSummary())));
			line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getReference())));
			line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getTextMsg())));
			table.addElement(line);
		}
		//
		b.addElement(table);
		link = new a ("#top", "Top");
		b.addElement(link);
		
		//	fini
		WebUtil.createResponse (request, response, this, null, doc, false);
		return true;
	}	//	processLogParameter
	
	/**
	 * 	Process Run Parameter
	 *	@param request request
	 *	@return true if it was a Run request
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private boolean processRunNowParameter (HttpServletRequest request)
		throws ServletException, IOException
	{
		String serverID = WebUtil.getParameter (request, "RunNow");
		if (serverID == null || serverID.length() == 0)
			return false;
		
		if (log.isLoggable(Level.INFO)) log.info ("ServerID=" + serverID);
		ServerWrapper server = m_serverMgr.getServer(serverID);
		if (server == null || server.getServer() == null)
		{
			m_message = new p();
			m_message.addElement(new strong("Server not found: "));
			m_message.addElement(serverID);
			return false;
		}
		//
		server.getServer().runNow();
		//
		return true;
	}	//	processRunParameter

	/**
	 * 	Process Action Parameter
	 *	@param request request
	 */
	private void processActionParameter (HttpServletRequest request,HttpServletResponse response)
	{
		String action = WebUtil.getParameter (request, "Action");
		if (action == null || action.length() == 0)
			return;
		if (log.isLoggable(Level.INFO)) log.info ("Action=" + action);
		try
		{
			boolean start = action.startsWith("Start");
			boolean reload=action.startsWith("Reload");
			m_message = new p();
			String msg = (start ? "Started" : "Stopped") + ": ";
			m_message.addElement(new strong(msg));
			//
			String serverID = action.substring(action.indexOf('_')+1);
			boolean ok = false;
			if (serverID.equals("All"))
			{
				if (start)
				{	
					ok = m_serverMgr.startAll();
				} else{					
					ok = m_serverMgr.stopAll();
				}
					
				m_message.addElement("All");
			}
			else
			{
				if (reload) 
				{
					ok=m_serverMgr.reload();
					this.createSummaryPage(request, response,true);
					m_dirAccessList = getDirAcessList();
				} else {
					ServerWrapper server = m_serverMgr.getServer(serverID);
					if (server == null || server.getServer() == null) {
						m_message = new p();
						m_message.addElement(new strong("Server not found: "));
						m_message.addElement(serverID);
						return;
					} else {
						if (start)
							ok = m_serverMgr.start(serverID);
						else
							ok = m_serverMgr.stop(serverID);
						m_message.addElement(server.getServer().getName());
					}
				}
			}
			m_message.addElement(ok ? " - OK" : " - Error!");
		}
		catch (Exception e)
		{
			m_message = new p();
			m_message.addElement(new strong("Error processing parameter: " + action));
			m_message.addElement(new br());
			m_message.addElement(e.toString());
		}
	}	//	processActionParameter

	/**
	 * 	Process Trace Parameter
	 *	@param request request
	 *	@param response response
	 *	@return true if it was a trace request with output
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private boolean processTraceParameter (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		String traceCmd = WebUtil.getParameter (request, "Trace");
		String traceLevel = WebUtil.getParameter (request, "TraceLevel");
		if (traceLevel != null && traceLevel.length() > 0)
		{
			if (log.isLoggable(Level.INFO)) log.info ("New Level: " + traceLevel);
			CLogMgt.setLevel(traceLevel);
			Ini.setProperty(Ini.P_TRACELEVEL, traceLevel);
			Ini.saveProperties(false);
			return false;
		}
		
		if (traceCmd == null || traceCmd.length() == 0)
			return false;
		
		if (log.isLoggable(Level.INFO)) log.info ("Command: " + traceCmd);
		CLogFile fileHandler = CLogFile.get (false, null, false);
		//
		if (traceCmd.equals("ROTATE"))
		{
			if (fileHandler != null)
				fileHandler.rotateLog();
			return false;	//	re-display
		}
		else if (traceCmd.equals("DELETE"))
		{
			File logDir = fileHandler.getLogDirectory();
			if (logDir != null && logDir.isDirectory())
			{
				File[] logs = logDir.listFiles();
				for (int i = 0; i < logs.length; i++) 
				{
					String fileName = logs[i].getAbsolutePath();
					if (fileName.equals(fileHandler.getFileName()))
						continue;
					if (fileName.endsWith(LogAuthFailure.authFailureFilename)) // Do not delete login failure
						continue;
					if (logs[i].delete())
						log.warning("Deleted: " + fileName);
					else
						log.warning("Not Deleted: " + fileName);
				}
			}
			return false;	//	re-display
		}
		
		//	Display current log File
		if (fileHandler != null && fileHandler.getFileName().equals(traceCmd))
			fileHandler.flush();
		
		//	Spool File
		File file = new File (traceCmd);
		if (!file.exists() || !file.canRead())
		{
			log.warning ("Did not find File: " + traceCmd);
			return false;
		}
		if (file.length() == 0)
		{
			log.warning ("File Length=0: " + traceCmd);
			return false;
		}

		boolean found = false;
		if (m_dirAccessList == null)
			m_dirAccessList = getDirAcessList();
		
		for (File dir : m_dirAccessList)
		{
			if (file.getCanonicalPath().startsWith(dir.getAbsolutePath()))
			{
				found = true;
				break;				
			}
		}
		
		if (!found)
		{
			log.warning ("Couldn't find file in directories that allowed to access");
			for (File dirAccess : m_dirAccessList)
				log.warning(" - " + dirAccess.getAbsoluteFile());
			return false;
		}
		
		//	Stream Log
		if (log.isLoggable(Level.INFO)) log.info ("Streaming: " + traceCmd);
		try
		{
			long time = System.currentTimeMillis();		//	timer start
			int fileLength = (int)file.length();
			int bufferSize = 2048; //	2k Buffer
			byte[] buffer = new byte[bufferSize];
			//
			FileInputStream fis = new FileInputStream(file);
			ServletOutputStream out = response.getOutputStream ();
			//
			response.setContentType("text/plain");
			response.setBufferSize(bufferSize);
			response.setContentLength(fileLength);
			int read = 0;
			while ((read = fis.read(buffer)) > 0)
				out.write (buffer, 0, read);
			out.flush();
			out.close();
			fis.close();
			//
			time = System.currentTimeMillis() - time;
			double speed = (fileLength/(double)1024) / (time/(double)1000);
			if (log.isLoggable(Level.INFO)) log.info("length=" 
				+ fileLength + " - " 
				+ time + " ms - " 
				+ speed + " kB/sec");
		}
		catch (Exception ex)
		{
			log.log(Level.SEVERE, "stream" + ex);
			return false;
		}
		return true;
	}	//	processTraceParameter
	
	/**
	 * 	Process EMail Parameter
	 *	@param request request
	 *	@param response response
	 *	@return true if it was a email request with output
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private boolean processEMailParameter (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		String email = WebUtil.getParameter (request, "EMail");
		if (email == null || email.length() == 0)
			return false;
		
		int AD_Client_ID = -1;
		try
		{
			AD_Client_ID = Integer.parseInt(email);
		}
		catch (Exception e)
		{
			log.warning("Parsing: " + email + " - " + e.toString());
		}
		if (AD_Client_ID < 0)
		{
			m_message = new p();
			m_message.addElement("No EMail: " + email);
			return false;
		}
		
	//	log.info ("Test EMail: " + AD_Client_ID);
		MClient client = MClient.get(new Properties(), AD_Client_ID);
		if (log.isLoggable(Level.INFO)) log.info ("Test: " + client);
		
		m_message = new p();
		m_message.addElement(client.getName() + ": " + client.testEMail());
		return false;
	}	//	processEMailParameter
	

	/**
	 * 	Process Cache Parameter
	 *	@param request request
	 *	@param response response
	 *	@return true if it was a email request with output
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private boolean processCacheParameter (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		String cmd = WebUtil.getParameter (request, "CacheReset");
		if (cmd == null || cmd.length() == 0)
			return false;
		String tableName = WebUtil.getParameter (request, "CacheTableName");
		String record_ID = WebUtil.getParameter (request, "CacheRecord_ID");
		
		m_message = new p();
		try
		{
			if (tableName == null || tableName.length() == 0)
			{
				CacheMgt.get().reset();
				m_message.addElement("Cache Reset: All");
			}
			else if (record_ID == null || record_ID.length() == 0)
			{
				CacheMgt.get().reset(tableName);
				m_message.addElement("Cache Reset: " + tableName);
			}
			else
			{
				CacheMgt.get().reset(tableName, Integer.parseInt(record_ID));
				m_message.addElement("Cache Reset: " + tableName + ", Record_ID=" + record_ID);
			}
		}
		catch (Exception e)
		{
			log.severe(e.toString());
			m_message.addElement("Error: " + e.toString());
		}
		return false;	//	continue
	}	//	processEMailParameter

	/**************************************************************************
	 * 	Create & Return Summary Page
	 *	@param request request
	 *	@param response response
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private void createSummaryPage (HttpServletRequest request, HttpServletResponse response,boolean refresh)
		throws ServletException, IOException
	{
		WebDoc doc = WebDoc.create ("iDempiere Server Monitor");
	//	log.info("ServletConfig=" + getServletConfig());
		AdempiereServerGroup.get().dump();

		//	Body
		body bb=new body();
		bb = doc.getBody();			
		
		//	Message
		if (m_message != null)
		{
			bb.addElement(new hr());
			bb.addElement(m_message);
			bb.addElement(new hr());
		}
		
		//	Summary
		table table = new table();
		table.setBorder(1);
		table.setCellSpacing(2);
		table.setCellPadding(2);
		//
		tr line = new tr();
		line.addElement(new th().addElement(Adempiere.getName()));
		line.addElement(new td().addElement(Adempiere.getVersion()));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement(Adempiere.getImplementationVendor()));
		line.addElement(new td().addElement(Adempiere.getImplementationVersion()));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("Manager"));
		line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getDescription())));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("Start - Elapsed"));
		line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getStartTime())
			+ " - " + TimeUtil.formatElapsed(m_serverMgr.getStartTime())));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("Servers"));
		line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getServerCount())));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("Last Updated"));
		line.addElement(new td().addElement(new Timestamp(System.currentTimeMillis()).toString()));
		table.addElement(line);
		bb.addElement(table);
		//
		p para = new p();
		a link = new a ("idempiereMonitor?Action=Start_All", "Start All");
		para.addElement(link);
		para.addElement(" - ");
		link = new a ("idempiereMonitor?Action=Stop_All", "Stop All");
		para.addElement(link);
		para.addElement(" - ");
		link = new a ("idempiereMonitor?Action=Reload", "Reload");
		para.addElement(link);
		para.addElement(" - ");
		link = new a ("idempiereMonitor", "Refresh");
		para.addElement(link);
		bb.addElement(para);
		
		//	***** Server Links *****			
		bb.addElement(new hr());
		para = new p();
		ServerWrapper[] servers = m_serverMgr.getAll();		
		for (int i = 0; i < servers.length; i++)
		{
			if (i > 0)
				para.addElement(new br());
			ServerWrapper server = servers[i];
			link = new a ("#" + server.getServer().getServerID(), server.getServer().getName());
			para.addElement(link);
			font status = null;
			if (server.isAlive())
				status = new font().setColor(HtmlColor.GREEN).addElement(" (Running)");
			else
				status = new font().setColor(HtmlColor.RED).addElement(" (Stopped)");
			para.addElement(status);
		}
		bb.addElement(para);

		//	**** Log Management ****	
		createLogMgtPage(bb);	
		
		//	***** Server Details *****
		bb.removeEndEndModifier();
		for (int i = 0; i < servers.length; i++)
		{
			ServerWrapper server = servers[i];
			bb.addElement(new hr());
			bb.addElement(new a().setName(server.getServer().getServerID()));
			bb.addElement(new h2(server.getServer().getName()));
			//
			table = new table();
			table.setBorder(1);
			table.setCellSpacing(2);
			table.setCellPadding(2);
			//	Status
			line = new tr();
			if (server.isAlive())
			{
				String msg = "Stop";
				link = new a ("idempiereMonitor?Action=Stop_" + server.getServer().getServerID(), msg);
				if (server.getServer().isSleeping())
				{
					line.addElement(new th().addElement("Sleeping"));
					line.addElement(new td().addElement(link));
				}
				else
				{
					line.addElement(new th().addElement("Running"));
					line.addElement(new td().addElement(link));
				}
				table.addElement(line);
				line = new tr();
				line.addElement(new th().addElement("Start - Elapsed"));
				line.addElement(new td().addElement(WebEnv.getCellContent(server.getServer().getStartTime()) 
					+ " - " + TimeUtil.formatElapsed(server.getServer().getStartTime())));
			}
			else
			{
				String msg = "Start";
				line.addElement(new th().addElement("Not Started"));
				link = new a ("idempiereMonitor?Action=Start_" + server.getServer().getServerID(), msg);
				line.addElement(new td().addElement(link));
			}
			table.addElement(line);
			//
			line = new tr();
			line.addElement(new th().addElement("Description"));
			line.addElement(new td().addElement(WebEnv.getCellContent(server.getServer().getDescription())));
			table.addElement(line);
			//
			line = new tr();
			line.addElement(new th().addElement("Last Run"));
			line.addElement(new td().addElement(WebEnv.getCellContent(server.getServer().getDateLastRun())));
			table.addElement(line);
			line = new tr();
			line.addElement(new th().addElement("Info"));
			line.addElement(new td().addElement(WebEnv.getCellContent(server.getServer().getServerInfo())));
			table.addElement(line);
			//
			line = new tr();
			line.addElement(new th().addElement("Next Run"));
			td td = new td();
			td.addElement(WebEnv.getCellContent(server.getServer().getDateNextRun(false)));
			td.addElement(" - ");
			link = new a ("idempiereMonitor?RunNow=" + server.getServer().getServerID(), "(Run Now)");
			td.addElement(link);
			line.addElement(td);
			table.addElement(line);
			//
			line = new tr();
			line.addElement(new th().addElement("Statistics"));
			line.addElement(new td().addElement(server.getServer().getStatistics()));
			table.addElement(line);
			//
			
			//	Add table to Body
			bb.addElement(table);
			link = new a ("#top", "Top");
			bb.addElement(link);
			bb.addElement(" - ");
			link = new a ("idempiereMonitor?Log=" + server.getServer().getServerID(), "Log");
			bb.addElement(link);
			bb.addElement(" - ");
			link = new a ("idempiereMonitor", "Refresh");
			bb.addElement(link);
		}

		//	fini
		WebUtil.createResponse (request, response, this, null, doc, false);
	}	//	createSummaryPage
	
	/**************************************************************************
	 * 	Create & Return Summary Page
	 *	@param request request
	 *	@param response response
	 *	@throws ServletException
	 *	@throws IOException
	 */
	private void createXMLSummaryPage (HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		response.setContentType("text/xml");
		PrintWriter writer = response.getWriter();

		writer.println("<server-response>");

		//	message
		writer.println("\t<message>");
		if (m_message != null)
		{
			writer.println(m_message);
		}
		writer.println("\t</message>");
		
		//	Summary
		writer.print("\t<name>");
		writer.print(Adempiere.getName());
		writer.println("</name>");
		
		writer.print("\t<version>");
		writer.print(Adempiere.getVersion());
		writer.println("</version>");

		writer.print("\t<implementation-vendor>");
		writer.print(Adempiere.getImplementationVendor());
		writer.println("</implementation-vendor>");
		
		writer.print("\t<implementation-version>");
		writer.print(Adempiere.getImplementationVersion());
		writer.println("</implementation-version>");

		writer.println("\t<server-manager>");
		writer.print("\t\t<description>");
		writer.print(m_serverMgr.getDescription());
		writer.println("</description>");
		writer.print("\t\t<start-time>");
		writer.print(m_serverMgr.getStartTime());
		writer.println("</start-time>");
		writer.print("\t\t<server-count>");
		writer.print(m_serverMgr.getServerCount());
		writer.println("</server-count>");
		
		ServerWrapper[] servers = m_serverMgr.getAll();		
		for (int i = 0; i < servers.length; i++)
		{
			ServerWrapper server = servers[i];
			writer.println("\t\t<server>");
			writer.print("\t\t\t<id>");
			writer.print(server.getServer().getServerID());
			writer.println("</id>");
			writer.print("\t\t\t<name>");
			writer.print(server.getServer().getName());
			writer.println("</name>");
			writer.print("\t\t\t<description>");
			writer.print(server.getServer().getDescription());
			writer.println("</description>");
			writer.print("\t\t\t<info>");
			writer.print(server.getServer().getServerInfo());
			writer.println("</info>");
			writer.print("\t\t\t<status>");
			if (server.isAlive())
			{
				if (server.isInterrupted())
					writer.print("Interrupted");
				else if (server.getServer().isSleeping())
					writer.print("Sleeping");
				else
					writer.print("Running");
			}
			else
				writer.print("Stopped");
			writer.println("</status>");
			writer.print("\t\t\t<start-time>");
			writer.print(server.getServer().getStartTime());
			writer.println("</start-time>");
			writer.print("\t\t\t<last-run>");
			writer.print(server.getServer().getDateLastRun());
			writer.println("</last-run>");
			writer.print("\t\t\t<next-run>");
			writer.print(server.getServer().getDateNextRun(false));
			writer.println("</next-run>");
			writer.print("\t\t\t<statistics>");
			writer.print(server.getServer().getStatistics());
			writer.println("</statistics>");
			writer.println("\t\t</server>");
		}
		
		writer.println("\t</server-manager>");
		
		writer.flush();
	}	//	createSummaryPage
	
	/**
	 * 	Add Log Management to page
	 *	@param bb body
	 */
	private void createLogMgtPage (body bb)
	{
		bb.addElement(new hr());
		
		//	Ini Parameters
		table table = new table();
		table.setBorder(1);
		table.setCellSpacing(2);
		table.setCellPadding(2);
		//
		Properties ctx = new Properties();
		MSystem system = MSystem.get(ctx);
		tr line = new tr();
		line.addElement(new th().addElement(system.getDBAddress()));
		line.addElement(new td().addElement(Ini.getAdempiereHome()));
		table.addElement(line);
		//	OS + Name
		line = new tr();
		String info = System.getProperty("os.name")
			+ " " + System.getProperty("os.version");
		String s = System.getProperty("sun.os.patch.level");
		if (s != null && s.length() > 0)
			info += " (" + s + ")";
		line.addElement(new th().addElement(info));
		info = system.getName();
		if (system.getCustomPrefix() != null)
			info += " (" + system.getCustomPrefix() + ")"; 
		line.addElement(new td().addElement(info));
		table.addElement(line);
		//	Java + email
		line = new tr();
		info = System.getProperty("java.vm.name")
			+ " " + System.getProperty("java.vm.version");
		line.addElement(new th().addElement(info));
		line.addElement(new td().addElement(system.getUserName()));
		table.addElement(line);
		//	DB + Instance
		line = new tr();
		CConnection cc = CConnection.get();
		AdempiereDatabase db = cc.getDatabase();
		info = db.getDescription();
		line.addElement(new th().addElement(info));
		line.addElement(new td().addElement(cc.getConnectionURL()));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("DB Connection Pool"));
		line.addElement(new td().addElement(cc.getDatabase().getStatus()));
		table.addElement(line);
		//	Processors/Support
		line = new tr();
		line.addElement(new th().addElement("Processor/Support"));
		line.addElement(new td().addElement(system.getNoProcessors() + "/" + system.getSupportUnits()));
		table.addElement(line);
		//	Memory
		line = new tr();
		MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
		line.addElement(new th().addElement("VM Memory"));
		line.addElement(new td().addElement(new CMemoryUsage(memory.getNonHeapMemoryUsage()).toString()));
		table.addElement(line);
		line = new tr();
		line.addElement(new th().addElement("Heap Memory"));
		line.addElement(new td().addElement(new CMemoryUsage(memory.getHeapMemoryUsage()).toString()));
		table.addElement(line);
		//	Runtime
		line = new tr();
		RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
		line.addElement(new th().addElement("Runtime " + rt.getName()));
		line.addElement(new td().addElement(TimeUtil.formatElapsed(rt.getUptime())));
		table.addElement(line);
		//	Threads
		line = new tr();
		ThreadMXBean th = ManagementFactory.getThreadMXBean();
		line.addElement(new th().addElement("Threads " + th.getThreadCount()));
		line.addElement(new td().addElement("Peak=" + th.getPeakThreadCount() 
			+ ", Demons=" + th.getDaemonThreadCount()
			+ ", Total=" + th.getTotalStartedThreadCount()));
		table.addElement(line);
		
		//Transactions
		Trx[] trxs = Trx.getActiveTransactions();
		for (Trx trx : trxs)
		{
			if (trx != null && trx.isActive()) 
			{
				line = new tr();
				line.addElement(new th().addElement("Active Transaction "));
				line.addElement(new td().addElement("Name="+trx.getTrxName() 
					+ ", StartTime=" + trx.getStartTime()));
				table.addElement(line);
			}
		}
		
		//	Cache Reset
		line = new tr();
		line.addElement(new th().addElement(CacheMgt.get().toStringX()));
		line.addElement(new td().addElement(new a ("idempiereMonitor?CacheReset=Yes", "Reset Cache")));
		table.addElement(line);
		
		//	Trace Level
		line = new tr();
		line.addElement(new th().addElement(new label("TraceLevel").addElement("Trace Log Level")));
		form myForm = new form("idempiereMonitor", form.METHOD_POST, form.ENC_DEFAULT);
		//	LogLevel Selection
		option[] options = new option[CLogMgt.LEVELS.length];
		for (int i = 0; i < options.length; i++) 
		{
			options[i] = new option(CLogMgt.LEVELS[i].getName());
			options[i].addElement(CLogMgt.LEVELS[i].getName());
			if (CLogMgt.LEVELS[i] == CLogMgt.getLevel())
				options[i].setSelected(true);
		}
		select sel = new select("TraceLevel", options);
		myForm.addElement(sel);
		myForm.addElement(new input(input.TYPE_SUBMIT, "Set", "Set"));
		line.addElement(new td().addElement(myForm));
		table.addElement(line);
		//
		line = new tr();
		CLogFile fileHandler = CLogFile.get (true, null, false);
		line.addElement(new th().addElement("Trace File"));
		line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=" + fileHandler.getFileName(), "Current")));
		table.addElement(line);
		//
		line = new tr();
		line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=ROTATE", "Rotate Trace Log")));
		line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=DELETE", "Delete all Trace Logs")));
		table.addElement(line);
		//
		bb.addElement(table);
		
		//	List Log Files
		p p = new p();
		p.addElement(new b("All Log Files: "));
		//	All in dir
		File logDir = fileHandler.getLogDirectory();
		if (logDir != null && logDir.isDirectory())
		{
			File[] logs = logDir.listFiles();
			for (int i = 0; i < logs.length; i++) 
			{
				// Skip if is not a file - teo_sarca [ 1726066 ]
				if (!logs[i].isFile())
					continue;
				
				if (i != 0)
					p.addElement(" - ");
				String fileName = logs[i].getAbsolutePath();
				a link = new a ("idempiereMonitor?Trace=" + fileName, fileName);
				p.addElement(link);
				int size = (int)(logs[i].length()/1024);
				if (size < 1024)
					p.addElement(" (" + size + "k)");
				else
					p.addElement(" (" + size/1024 + "M)");
			}
		}
		bb.addElement(p);
		
		//	Clients and Web Stores
		table = new table();
		table.setBorder(1);
		table.setCellSpacing(2);
		table.setCellPadding(2);
		//	
		line = new tr();
		MClient[] clients = MClient.getAll(ctx, "AD_Client_ID");
		line.addElement(new th().addElement("Client #" + clients.length + " - EMail Test:"));
		p = new p();
		for (int i = 0; i < clients.length; i++)
		{
			MClient client = clients[i];
			if (i > 0)
				p.addElement(" - ");
			p.addElement(new a("idempiereMonitor?EMail=" + client.getAD_Client_ID(), client.getName()));
		}
		if (clients.length == 0)
			p.addElement("&nbsp;");
		line.addElement(new td().addElement(p));
		table.addElement(line);
		//	
		line = new tr();
		MStore[] wstores = MStore.getActive();
		line.addElement(new th().addElement("Active Web Stores #" + wstores.length));
		p = new p();
		for (int i = 0; i < wstores.length; i++)
		{
			MStore store = wstores[i];
			if (i > 0)
				p.addElement(" - ");
			a a = new a(store.getWebContext(), store.getName());
			a.setTarget("t" + i);
			p.addElement(a);
		}
		if (wstores.length == 0)
			p.addElement("&nbsp;");
		line.addElement(new td().addElement(p));
		table.addElement(line);
		//	
		line = new tr();
		List<MSession> sessions =  new Query(Env.getCtx(), MSession.Table_Name, "Processed = 'N'", null).list();
		line.addElement(new th().addElement("Active sessions #" + sessions.size()));
		p = new p();
		for (int i = 0; i < clients.length; i++) {
			MClient client = clients[i];
			if (!client.isActive())
				continue;
			if (i > 0)
				p.addElement(" - ");
			int count = 0;
			for (MSession session : sessions) {
				if (session.getAD_Client_ID()==client.getAD_Client_ID())
					count++;
			}
			p.addElement(client.getName() + " : " + count);
		}
		if (clients.length == 0)
			p.addElement("&nbsp;");
		line.addElement(new td().addElement(p));
		table.addElement(line);
		//	
		line = new tr();

		boolean isSystemInMaintenance = MSysConfig.getBooleanValue(MSysConfig.SYSTEM_IN_MAINTENANCE_MODE, false, 0);
		List<Integer> inMaintenanceClients = new ArrayList<Integer>();
		if (isSystemInMaintenance)
			line.addElement(new th().addElement("Maintenance Mode"));
		else {
			int possiblyInMaintenanceClients[] = DB.getIDsEx(null, "SELECT AD_Client_ID FROM AD_SysConfig WHERE AD_Client_ID!=0 AND IsActive='Y' AND Name=?", MSysConfig.SYSTEM_IN_MAINTENANCE_MODE);
			for (int clientId : possiblyInMaintenanceClients) {
				boolean isTenantInMaintenance = MSysConfig.getBooleanValue(MSysConfig.SYSTEM_IN_MAINTENANCE_MODE, false, clientId);
				if (isTenantInMaintenance)
					inMaintenanceClients.add(clientId);
			}
			line.addElement(new th().addElement("Maintenance Mode #"+inMaintenanceClients.size()));
		}

		p = new p();
		if (isSystemInMaintenance)
			p.addElement("All clients are in maintenance mode");
		else if (inMaintenanceClients.size() > 0) {
			boolean first = true;
			for (int clientID : inMaintenanceClients) {
				MClient client = MClient.get(ctx, clientID);
				if (!client.isActive())
					continue;
				if (!first)
					p.addElement(" - ");
				p.addElement(client.getName());
				first = false;
			}
		}
		else
			p.addElement("All clients are in normal operation mode");
		if (clients.length == 0)
			p.addElement("&nbsp;");
		line.addElement(new td().addElement(p));
		table.addElement(line);
		//
		bb.addElement(table);
	}	//	createLogMgtPage
	
	/**************************************************************************
	 * 	Init
	 *	@param config config
	 *	@throws javax.servlet.ServletException
	 */
	public void init (ServletConfig config) throws ServletException
	{
		WebEnv.initWeb(config);
		log.info ("");
		m_serverMgr = AdempiereServerMgr.get();
		m_dirAccessList = getDirAcessList();
	}	//	init
	
	/**
	 * 	Destroy
	 */
	public void destroy ()
	{
		log.info ("destroy");
		m_serverMgr = null;
		m_dirAccessList = null;
	}	//	destroy
	
	/**
	 * 	Log error/warning
	 *	@param message message
	 *	@param e exception
	 */
	public void log (String message, Throwable e)
	{
		if (e == null)
			log.warning (message);
		log.log(Level.SEVERE, message, e);
	}	//	log
	
	/**
	 * 	Log debug
	 *	@param message message
	 */
	public void log (String message)
	{
		log.fine(message);
	}	//	log

	
	/**
	 * 	Get Servlet Name
	 *	@return servlet name
	 */
	public String getServletName ()
	{
		return "AdempiereMonitor";
	}	//	getServletName

	/**
	 * 	Get Servlet Info
	 *	@return servlet info
	 */
	public String getServletInfo ()
	{
		return "iDempiere Server Monitor";
	}	//	getServletName

	private static final String s_dirAccessFileName = "dirAccess.txt";
	
	private ArrayList<File> getDirAcessList()
	{
		final ArrayList<File> dirAccessList = new ArrayList<File>();
		
		// by default has access to log directory
		CLogFile fileHandler = CLogFile.get (true, null, false);
		File logDir = fileHandler.getLogDirectory();
		dirAccessList.add(logDir);
		
		// load from dirAccess.properties file
		String dirAccessPathName = Adempiere.getAdempiereHome() + File.separator + s_dirAccessFileName;
		File dirAccessFile = new File(dirAccessPathName);
		if (dirAccessFile.exists()) 
		{
			try 
			{
				BufferedReader br = new BufferedReader(new FileReader(dirAccessFile));
		        while (true) {
		            String pathName = br.readLine();
		            if (pathName == null)
		            	break;
					File pathDir = new File(pathName);
					if (pathDir.exists() && !dirAccessList.contains(pathDir))
						dirAccessList.add(pathDir);
		        }
		        br.close();
			} 
			catch (Exception e) 
			{
				log.log(Level.SEVERE, dirAccessPathName + " - " + e.toString());
			}
		}
		/* -- uncomment to generate a default file
		else
		{
			try 
			{
				FileWriter fw = new FileWriter(dirAccessFile);
				fw.write(logDir.getCanonicalPath() + "\n");
				fw.close();
			} 
			catch (Exception e) 
			{
				log.log(Level.SEVERE, dirAccessPathName + " - " + e.toString());
			}
		}
		*/
				
		return dirAccessList;
	}
}	//	AdempiereMonitor
