/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.tools.admingui.handlers;

import com.iplanet.jato.RequestContext;
import com.iplanet.jato.RequestManager;
import com.iplanet.jato.model.DefaultModel;
import com.iplanet.jato.util.HtmlUtil;
import com.iplanet.jato.view.ContainerView;
import com.iplanet.jato.view.View;
import com.iplanet.jato.view.event.ChildContentDisplayEvent;

import com.sun.web.ui.model.CCPropertySheetModelInterface;
import com.sun.web.ui.model.CCPropertySheetModel;

import com.sun.enterprise.tools.admingui.util.MBeanUtil;
import com.sun.enterprise.tools.admingui.util.Util;
import com.sun.enterprise.tools.guiframework.exception.FrameworkException;
import com.sun.enterprise.tools.guiframework.view.DescriptorCCActionTable;
import com.sun.enterprise.tools.guiframework.view.HandlerContext;
import com.sun.enterprise.tools.guiframework.view.descriptors.ViewDescriptor;
import com.sun.enterprise.tools.guiframework.view.descriptors.CCPropertySheetDescriptor;
import com.sun.enterprise.tools.guiframework.view.DescriptorContainerView;
import com.sun.enterprise.tools.guiframework.view.DescriptorCCPageTitle;

import com.sun.web.ui.common.CCI18N;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Date;
import java.util.EventObject;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.io.File;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.ObjectName;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import java.lang.reflect.Method;

/**
 * LogViewerHandler
 */
public class LogViewerHandler {

    /**
     *	This is the main search/filter handler for the LogViewer
     */
    public void filter(RequestContext ctx, HandlerContext handlerCtx) {
	View view = handlerCtx.getView();
	ServletRequest request = ctx.getRequest();

	// Attempt to read values passed in
        String archivedLogFile = (String)handlerCtx.getInputValue(ARCHIVED_LOG_FILE);
	Long fromRecord = (Long)handlerCtx.getInputValue(FROM_RECORD);
	Boolean after = (Boolean)handlerCtx.getInputValue(AFTER_RECORD);
	Boolean dateEnabled = (Boolean)handlerCtx.getInputValue(DATE_ENABLED);
	Object fromDate = handlerCtx.getInputValue(FROM_DATE);
	Object fromTime = handlerCtx.getInputValue(FROM_TIME);
	Object toDate = handlerCtx.getInputValue(TO_DATE);
	Object toTime = handlerCtx.getInputValue(TO_TIME);
	Object loggers = handlerCtx.getInputValue(LOGGERS);
	Object logLevel = handlerCtx.getInputValue(LOG_LEVEL);
	Object customLoggers = handlerCtx.getInputValue(CUSTOM_LOGGERS);
	Object nvp = handlerCtx.getInputValue(NVP);
	Integer numberToDisplay = (Integer)handlerCtx.getInputValue(NUMBER_TO_DISPLAY);
	Boolean onlyLevel =
	    (Boolean)handlerCtx.getInputValue(ONLY_LEVEL);
	Boolean direction =
	    (Boolean)handlerCtx.getInputValue(LOG_DATE_SORT_DIRECTION);
	Boolean truncMsg =
	    (Boolean)handlerCtx.getInputValue(TRUNCATE_MESSAGE);
	Integer truncLenInteger =
	    (Integer)handlerCtx.getInputValue(TRUNCATE_LENGTH);
        String logManagerObjectName = (String)handlerCtx.getInputValue(LOG_MANAGER_OBJECT_NAME);
        String instanceName = (String)handlerCtx.getInputValue(INSTANCE_NAME);
        /*
        System.out.println("============ In LogViewerHandler: logManagerObjectName = " + logManagerObjectName);
	System.out.println("============ In LogViewerHandler: instanceName = " + instanceName);
        System.out.println("============ In LogViewerHandler: archivedLogFile = " + archivedLogFile);
         */

        if (instanceName==null || "".equals(instanceName)){
            //System.out.println("===== null instanceName, filter returns. ");
            handlerCtx.setOutputValue(FIRST_LOG_ROW, "-1");
	    handlerCtx.setOutputValue(LAST_LOG_ROW, "-1");
            return;
        }

        // Determine if messages should be truncated
	boolean truncateMessage = true;
	if (truncMsg != null) {
	    truncateMessage = truncMsg.booleanValue();
	}
	int truncLen = 100;
	if (truncLenInteger != null) {
	    truncLen = truncLenInteger.intValue();
	}

        //Determine if archived log file specified or use current log file
        if (archivedLogFile == null || archivedLogFile.equals("")) {
            archivedLogFile = null;
	} else {
            archivedLogFile= archivedLogFile;
	}

	// Allow the model to be specified (if not specified, the table model
	// will be used)
	DefaultModel model = (DefaultModel)handlerCtx.getInputValue(LOG_MODEL);
	DescriptorCCActionTable table =
	    (DescriptorCCActionTable)handlerCtx.getInputValue(TABLE);

	if ((table == null) && (model == null)) {
	    throw new FrameworkException("Input parameter '"+TABLE+
		"' must be specified with the log table View as the value, "+
		"or the model to populate should be passed in as Request "+
		"attribute '"+LOG_MODEL+"'.", null, view);
	}

	// Convert Date/Time fields
	boolean dateEnabledFlag = false;
	if (dateEnabled != null) {
	    dateEnabledFlag = dateEnabled.booleanValue();
	}
	if (dateEnabledFlag) {
	    // Date is enabled, figure out what the values are
	    fromDate = convertDateTime(
		request, fromDate, fromTime, table.getViewDescriptor(), view);
	    toDate = convertDateTime(
		request, toDate, toTime, table.getViewDescriptor(), view);
	    if ((fromDate == null) || (fromDate == null)) {
		throw new FrameworkException(
		    "'Specific Date Range' was chosen, however, date fields "+
		    "are incomplete.", null, view);
	    }
	    if (((Date)fromDate).after((Date)toDate)) {
		throw new FrameworkException(
		    "Timestamp value of 'From: ' field " + fromDate +
		    " must not be greater than 'To: ' field value " + toDate,
		    null, view);
	    }
	} else {
	    // Date not enabled, ignore from/to dates
	    fromDate = null;
	    toDate = null;
	}

	if (logLevel != null) {
	    if (logLevel.toString().trim().length() == 0) {
		logLevel = null;
	    }
	}

	if (onlyLevel == null) {
	    onlyLevel = FALSE;
	}

	// Convert module array to List
	List moduleList = null;
	if (loggers != null) {
	    int len = ((Object[])loggers).length;
	    moduleList = new ArrayList(len);
	    Object val;
	    for (int count=0; count<len; count++) {
		val = (((Object[])loggers)[count]);
		if ((val == null) || (val.toString().trim().length() == 0)) {
		    continue;
		}
		moduleList.add(val);
	    }
	}

	// Add custom loggers
	if ((customLoggers != null) &&
		(customLoggers.toString().trim().length() != 0)) {
	    StringTokenizer tok = new StringTokenizer(
		customLoggers.toString(),
		CUSTOM_LOGGER_DELIMITERS);
	    String token;
	    while (tok.hasMoreTokens()) {
		token = tok.nextToken();
		if ((token == null) || (token.length()==0)) {
		    continue;
		}
		moduleList.add(token);
	    }
	}


	// Deal w/ NVPs
	Hashtable nvpProps = null;
	if ((nvp != null) && (nvp.toString().trim().length() != 0)) {
	    nvpProps = new Properties();
	    int equalsIdx;
	    String token;
	    // Iterate over the entries
	    StringTokenizer tok =
		new StringTokenizer(nvp.toString(), NVP_DELIMITERS);
	    while (tok.hasMoreTokens()) {
		token = tok.nextToken();
		if ((token == null) || (token.length()==0)) {
		    continue;
		}
		equalsIdx = token.indexOf(EQUALS);
		// Make sure = exists and it is not the first character
		if (equalsIdx < 0) {
		    throw new FrameworkException(
			"Name-Value Pairs must be in the format \""+
			"<name>=<value>\".", null, view);
		}

		// Add this to the Hashtable
                String key = token.substring(0, equalsIdx++);
                ArrayList valueList = (ArrayList) nvpProps.get(key);
                if (valueList == null){
                     valueList = new ArrayList();
                     valueList.add(token.substring(equalsIdx));
                     nvpProps.put(key, valueList);
                }else{
                    valueList.add(token.substring(equalsIdx));
                }
	    }
            //nvpProps.list(System.out);
	}


	// Get the number to Display
	if (numberToDisplay == null) {
	    numberToDisplay = DEFAULT_NUMBER_TO_DISPLAY;
	}

	// Get the direction
	if (direction == null) {
	    direction = FALSE;
	}

	// Get AfterRecord flag
	if (after == null) {
	    // Not supplied, use direction
	    after = direction;
	}

	// Build the params object[]
	Object params[] = new Object[QUERY_SIGNATURE.length];
        params[0] = archivedLogFile;    //log file name. if null, use current.
	params[1] = fromRecord;		// fromRecord
	params[2] =			// next (after or before fromRecord)
	    (fromRecord == null) ? direction : after;
	params[3] = direction;		// forward
	params[4] = numberToDisplay;	// requestedCount
	params[5] = fromDate;		// fromDate
	params[6] = toDate;		// toDate
	params[7] = logLevel;		// logLevel
	params[8] = onlyLevel;		// onlyLevel
	params[9] = moduleList;		// listOfModules
	params[10] = nvpProps;		// nameValueMap


	// Search for the log entries
	AttributeList results = null;
	try {
            results = (AttributeList) getLogRecordsUsingQuery(logManagerObjectName, instanceName, params, QUERY_SIGNATURE);
	}catch (Exception ex) {
	    throw new FrameworkException("Error while querying Log File.",
		ex, (table == null) ? null : table.getViewDescriptor(), view);
	}

	// Add the results to the Model
	if (model == null) {
	    model = (DefaultModel)table.getModel();
	}

	// Get rid of anything that is already in the Model
	model.clear();

	String message;
	List headerRow = (List)(((Attribute)results.get(0)).getValue());
	List rowList = (List)(((Attribute)results.get(1)).getValue());
	List row;
	Iterator it = rowList.iterator();
	while (it.hasNext()) {
	    row = (List)it.next();
	    if (row.size() != headerRow.size()) {
		throw new FrameworkException(
		    "Row had '"+row.size()+"' columns, header has '"+
		    headerRow.size()+"' columns!", table.getViewDescriptor(),
		    view);
	    }
	    model.appendRow();

	    // FIXME: Use the Header row to pull off right stuff (vs. indexes)

	    model.setValue("recNumber", row.get(0));
	    /*
            if (archivedLogFile != null) {
		":"+instanceName+":"+archivedLogFile);
                model.setValue("recNumber", row.get(0)+":"+instanceName+":"+archivedLogFile);
	    } else {
                model.setValue("recNumber", row.get(0)+":"+instanceName+":");
	    }
	    */
	    model.setValue("dateTime", formatDateForDisplay(
		request.getLocale(), (Date)row.get(1)));
            String msgId = (String) row.get(6);
            //System.out.println("msgId=" + msgId);
	    // Set the image if appropriate
	    String level = (String)row.get(2);
            //only SEVERE msg provoides diagnostic info.
	    if (level.equalsIgnoreCase("severe")) {
		// NOTE: Image name/location is hard-coded
		model.setValue("levelImage", Util.getMessage("common.errorGif"));
		model.setValue(SHOW_LEVEL_IMAGE, new Boolean(true));
                model.setValue("diagnosticCauses", getDiagnosticCauses(msgId));
                model.setValue("diagnosticChecks", getDiagnosticChecks(msgId));
                model.setValue("diagnosticURI", getDiagnosticURI(msgId));
                //System.out.println("=== after setting diagnostic for msgId=" + msgId);
	    } else {
		model.setValue(SHOW_LEVEL_IMAGE, new Boolean(false));
                model.setValue("diagnostic", "");
	    }
	    model.setValue("level", level);
	    model.setValue("productName", row.get(3));
	    model.setValue("logger", row.get(4));
	    model.setValue("nvp", row.get(5));
	    model.setValue("messageID", msgId );
	    message = ((String)row.get(7)).trim();
	    if (truncateMessage && (message.length() > truncLen)) {
		message = message.substring(0, truncLen).concat("...\n");
	    }
	    model.setValue("message", formatMessageForDisplay(message));
	}

	// Set the first / last record numbers as attributes
	if (rowList.size() > 0) {
	    handlerCtx.setOutputValue(FIRST_LOG_ROW,
		((List)rowList.get(0)).get(0));
	    handlerCtx.setOutputValue(LAST_LOG_ROW,
		((List)rowList.get(rowList.size()-1)).get(0));
	} else {
	    handlerCtx.setOutputValue(FIRST_LOG_ROW, "-1");
	    handlerCtx.setOutputValue(LAST_LOG_ROW, "-1");
	}
    }


    /**
     *	This handler generates the HTML that appears on the bottom of the
     *	table for navigation.  This handler is only valid for endDisplay.
     */
    public String generateResultNavBar(RequestContext ctx, HandlerContext handlerCtx) {
	if (!(handlerCtx.getEvent() instanceof ChildContentDisplayEvent)) {
	    throw new FrameworkException(getClass().getName()+
		".generateResultNavBar is only valid for endDisplay events.",
		null, handlerCtx.getView());
	}

	// Get the first/last row numbers
	String firstLogRow = (String)handlerCtx.getInputValue(FIRST_LOG_ROW);
	String lastLogRow = (String)handlerCtx.getInputValue(LAST_LOG_ROW);
        if (firstLogRow == null) {
            firstLogRow="0";
	}
        if (lastLogRow == null) {
            lastLogRow="0";
	}
	int firstRow = 0;
	try {
	    firstRow = Integer.parseInt(firstLogRow);
	    int lastRow = Integer.parseInt(lastLogRow);
	    if (firstRow > lastRow) {
		String temp = firstLogRow;
		firstLogRow = lastLogRow;
		lastLogRow = temp;
		firstRow = lastRow;
	    }
	} catch (NumberFormatException ex) {
	    // ignore
	}

	// Next get the button Strings
	String prevButtonString = (String)handlerCtx.getInputValue(PREV_BUTTON_STRING);
	String nextButtonString = (String)handlerCtx.getInputValue(NEXT_BUTTON_STRING);

	// Get the localized strings that we need
	// Try to get the resource bundle
	String bundle = (String)handlerCtx.getInputValue(RESOURCE_BUNDLE);
	if (bundle == null) {
	    bundle = handlerCtx.getViewDescriptor().getResourceBundle();
	}
	if (bundle == null) {
	    throw new FrameworkException("You MUST specify the resource "+
		"bundle via input parameter: '"+RESOURCE_BUNDLE+"'.",
		null, handlerCtx.getView());
	}
	CCI18N i18n = new CCI18N(ctx, bundle);

// ResourceBundle bundle = table.getModel().getResourceBundle();
// String priorMatches = bundle.getString("logViewer.priorMatches");
// String logResultSummary = bundle.getString("logViewer.logResultSummary");
// String logResultSummaryThrough = bundle.getString("logViewer.logResultSummaryThrough");
// String nextMatches = bundle.getString("logViewer.nextMatches");
	String priorMatches = i18n.getMessage("logViewer.priorMatches");
	String logResultSummary = i18n.getMessage("logViewer.logResultSummary");
	String logResultSummaryThrough = i18n.getMessage("logViewer.logResultSummaryThrough");
	String nextMatches = i18n.getMessage("logViewer.nextMatches");

        StringBuffer content =
	    new StringBuffer(((ChildContentDisplayEvent)handlerCtx.getEvent()).getContent());
	int mark = content.lastIndexOf("</table>");
	if (mark > 0) {
// Insert new tr w/ colspan 6??
	    StringBuffer newRow = new StringBuffer("\n");
	    newRow.append("\t    <tr><td class=\"TblActTd\" colspan=\"6\" "
		    + "nowrap=\"nowrap\">\n");
	    newRow.append("\t\t<div style=\"margin-top:6px;\">\n");

	    // UP Button
	    if (firstRow > 0) {
		newRow.append(prevButtonString);
	    }

	    newRow.append("\t\t    <img src=\"../images/spacer.gif\" "
		    + "alt=\"\" width=\"10\" height=\"8\" />\n");
	    newRow.append("\t\t    <span class=\"LblLev2Txt\">\n");
	    newRow.append("\t\t\t<label for=\"page\">"+logResultSummary+" "+
		firstLogRow+" "+logResultSummaryThrough+" "+
		lastLogRow+"</label>\n");
	    newRow.append("\t\t    </span><img src=\"../images/spacer.gif\""
		    + "alt=\"\" width=\"10\" height=\"1\" />\n");

	    // DOWN Button
	    newRow.append(nextButtonString);

	    newRow.append("\t\t</div></td></tr>\n");

	    // Insert at the end of the Table
	    content.insert(mark, newRow.toString());

	    // Try to insert the same row at the top of the table
	    // WARNING: The following is fragile and replies on the current
	    //		implementation of the LH table
	    mark = content.indexOf("<tr>");
	    if (mark > 0) {
		// Try to add in the button row before the first <tr>
		content.insert(mark, newRow.toString());
	    }
	}

	// Return the result
	return content.toString();
    }


    /**
     *	<P>This method returns the current date (as a String).  The DATE_FORMAT
     *	must be specified, if it is not this method will fail.  You may set it
     *	to "short", "medium", "long", or "FULL".</P>
     *
     *	<P>If you do not set it to one of these values, you may set it to a
     *	valid format string.</P>
     */
    public void getDate(RequestContext ctx, HandlerContext handlerCtx) {
    	// Get the required attribute
	String formatString = (String)handlerCtx.getInputValue(DATE_FORMAT);
	if ((formatString == null) || (formatString.trim().length() == 0)) {
	    throw new FrameworkException("You MUST specify the attribute: '"+
	    	DATE_FORMAT+"' in order for getDate to work!",
		null, handlerCtx.getView());
	}

	// Get the type
	int formatType = -1;
	if (formatString.equals(GET_DATE_SHORT)) {
	    formatType = DateFormat.SHORT;
	} else if (formatString.equals(GET_DATE_MEDIUM)) {
	    formatType = DateFormat.MEDIUM;
	} else if (formatString.equals(GET_DATE_LONG)) {
	    formatType = DateFormat.LONG;
	} else if (formatString.equals(GET_DATE_FULL)) {
	    formatType = DateFormat.FULL;
	}
	DateFormat df = null;
	if (formatType == -1) {
	    df = DateFormat.getDateInstance(
		DateFormat.SHORT, ctx.getRequest().getLocale());
	    ((SimpleDateFormat)df).applyLocalizedPattern(formatString);
	} else {
	    df = DateFormat.getDateInstance(
		formatType, ctx.getRequest().getLocale());
	}

	// Set the return value
	handlerCtx.setOutputValue(DATE_VALUE, df.format(new Date()));
    }

      public void getFormatedDateTime(RequestContext ctx, HandlerContext handlerCtx) {
          String ts = (String )handlerCtx.getInputValue("timestamp");
          Date date = null;
          if (ts == null || "".equals(ts)){
            date = new Date(System.currentTimeMillis());
          }else{
              date = new Date( Long.parseLong(ts));
          }
          DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, ctx.getRequest().getLocale());  
          DateFormat tf = DateFormat.getTimeInstance(DateFormat.MEDIUM, ctx.getRequest().getLocale());
          ((SimpleDateFormat)tf).applyLocalizedPattern(" HH:mm:ss.SSS");
          
          String ftime = tf.format(date);
          String fdate = df.format(date);
          handlerCtx.setOutputValue("valueTime", ftime);
          handlerCtx.setOutputValue("valueDate", fdate);
       }      

    
    /**
     *	<P>This method puts the current time (as a String) in the desired
     *	attribute.  The result attribute must be specified via an attribute
     *	named "getTimeResultAttribute"</P>
     */
    public void getTime(RequestContext ctx, HandlerContext handlerCtx) {
	DateFormat df = DateFormat.getTimeInstance(
	    DateFormat.SHORT, ctx.getRequest().getLocale());
	((SimpleDateFormat)df).applyLocalizedPattern(TIME_FORMAT);

	// Set the return value
	handlerCtx.setOutputValue(GET_TIME_RESULT, df.format(new Date()));
    }
    
    /**
     *	<P>This method returns the current log file name stored the log-service config element </P>
     */
    public void getCurrentLogFileName(RequestContext ctx, HandlerContext handlerCtx) {
       String instanceName = (String )handlerCtx.getInputValue("instanceName");
       String configName = (String)MBeanUtil.getAttribute("com.sun.appserv:type=server,category=config,name="+instanceName, "config-ref");
       String fileName = (String) MBeanUtil.getAttribute("com.sun.appserv:type=log-service,category=config,config="+configName, "file");
       File file = new File(fileName);
       String fn = file.getName();
	// Set the return value
	handlerCtx.setOutputValue("currentLogFileName", fn);
    }


    /**
     *	This method formats the log message to be displayed appropriately for
     *	HTML (it converts '\n' characters to &lt;br&gt;'s.
     */
    protected String formatMessageForDisplay(String message) {
	return HtmlUtil.escape(message).replaceAll("\n", "<br>");
    }

    /**
     *  This method formats the diagnostic to be displayed for HTML
     *  Add '<br>' to each elements of the ArrayList and returns the String.
     */
    protected String formatArrayListForDisplay(ArrayList diag) {
        if ((diag == null) || (diag.size() == 0)) {
	    return "";
	}
        StringBuffer buf = new StringBuffer("<br>");
        for(int i=0; i<diag.size(); i++){
            buf.append( (String)diag.get(i));
	    buf.append("<br>");
        }
        return buf.toString();
    }

    /**
     *	This method formats a log file date to a more readable date (based on
     *	locale).
     */
    public static String formatDateForDisplay(Locale locale, Date date) {
	DateFormat dateFormat = DateFormat.getDateInstance(
	    DateFormat.MEDIUM, locale);
	if (dateFormat instanceof SimpleDateFormat) {
	    SimpleDateFormat fmt = (SimpleDateFormat)dateFormat;
	    fmt.applyLocalizedPattern(fmt.toLocalizedPattern()+TIME_FORMAT);
	    return fmt.format(date);
	} else {
	    dateFormat = DateFormat.getDateTimeInstance(
		DateFormat.MEDIUM, DateFormat.LONG, locale);
	    return dateFormat.format(date);
	}
    }


    /**
     *	This method converts a date/time string to a Date.
     *
     *	@param	request	The ServletRequest
     *	@param	date	The date as a String (or the date/time as a Date)
     *	@param	time	The time as a String (or null)
     *	@param	vd	The ViewDescriptor (for exception handling)
     *	@param	view	The View (for exception handling)
     */
    protected Date convertDateTime(
	    ServletRequest request, Object date, Object time,
	    ViewDescriptor vd, View view) {
    	// If Date is already a Date, then do nothing
	if (date instanceof Date) {
	    return (Date)date;
	}
	// If Date is null or empty, return null
	if ((date == null) || (date.toString().trim().length() == 0)) {
	    return null;
	}

	// Get the date / time string
	if (time.toString().trim().length() == 0) {
	    time = null;
	}
	String dateTime = date.toString()+
	    ((time == null) ? "" : (" "+time.toString()));
	DateFormat df = DateFormat.getDateInstance(
	    DateFormat.SHORT, request.getLocale());
	if ((time != null) && (df instanceof SimpleDateFormat)) {
	    SimpleDateFormat fmt = (SimpleDateFormat)df;
	    String formatPrefix = fmt.toLocalizedPattern();
	    try {
		// Try w/ HH:mm:ss.SSS
		date = parseDateString(
		    fmt, formatPrefix+TIME_FORMAT, dateTime);
	    } catch (ParseException ex) {
		try {
		    // Try w/ HH:mm:ss
		    date = parseDateString(
			fmt, formatPrefix+TIME_FORMAT_2, dateTime);
		} catch (ParseException ex2) {
		    try {
			// Try w/ HH:mm
			date = parseDateString(
			    fmt, formatPrefix+TIME_FORMAT_3, dateTime);
		    } catch (ParseException ex3) {
			throw new FrameworkException(
			    "Unable to parse Date/Time: '"+dateTime+"'.",
			    ex3, vd, view);
		    }
		}
	    }
	} else if (time != null) {
	    // I don't think this ever happens
	    df = DateFormat.getDateTimeInstance(
		DateFormat.SHORT, DateFormat.LONG, request.getLocale());
	    try {
		date = df.parse(dateTime);
	    } catch (ParseException ex) {
		throw new FrameworkException(
		    "Unable to parse Date/Time: '"+dateTime+"'.",
		    ex, vd, view);
	    }
	} else {
	    try {
		date = df.parse(dateTime);
	    } catch (ParseException ex) {
		throw new FrameworkException(
		    "Unable to parse Date/Time: '"+dateTime+"'.",
		    ex, vd, view);
	    }
	}

	// Return the result
	return (Date)date;
    }


    /**
     *	This method simply takes the given SimpleDateFormat and parses the
     *	given String after applying the given format String.
     */
    private Date parseDateString(SimpleDateFormat fmt, String format, String dateTime) throws ParseException {
	fmt.applyLocalizedPattern(format);
	return fmt.parse(dateTime);
    }

    /**
     * * This method get the diagnostic based on the message id
     */
    private String getDiagnosticCauses(String msgId){

        if (msgId == null || "".equals(msgId))
            return formatArrayListForDisplay(null);
        String params[] = {msgId};
        String signatures[] = {"String"};
        try {
	    ArrayList results = (ArrayList)MBeanUtil.invoke(
		"com.sun.appserv:name=logmanager,category=runtime,server=server",
		"getDiagnosticCausesForMessageId",
		params,
		signatures);
             String res =  formatArrayListForDisplay(results);
             return res;
	} catch (Exception ex) {
	    throw new FrameworkException("Error while retrieving diagnostic from messageId.", ex);
	}
    }

   /**
     * * This method get the diagnostic Checks based on the message id
     */
    private String getDiagnosticChecks(String msgId){

        if (msgId == null || "".equals(msgId))
            return formatArrayListForDisplay(null);
        String params[] = {msgId};
        String signatures[] = {"String"};
        try {
	    ArrayList results = (ArrayList)MBeanUtil.invoke(
		"com.sun.appserv:name=logmanager,category=runtime,server=server",
		"getDiagnosticChecksForMessageId",
		params,
		signatures);
             String res =  formatArrayListForDisplay(results);
             return res;
	} catch (Exception ex) {
	    throw new FrameworkException("Error while retrieving diagnostic checks from messageId.", ex);
	}
    }

    /**
     * * This method get the diagnostic Checks based on the message id
     */
    private String getDiagnosticURI(String msgId){

        if (msgId == null || "".equals(msgId))
            return "";
        String params[] = {msgId};
        String signatures[] = {"String"};
        try {
	    String res = (String)MBeanUtil.invoke(
		"com.sun.appserv:name=logmanager,category=runtime,server=server",
		"getDiagnosticURIForMessageId",
		params,
		signatures);
            return res;
            //return "http://www.google.com";
	} catch (Exception ex) {
	    throw new FrameworkException("Error while retrieving diagnostic URI from messageId.", ex);
	}
    }


    /**
     *	This handler returns true if there are results to display, false
     *	otherwise.
     */
    public boolean hasResults(RequestContext ctx, HandlerContext handlerCtx) {
	DescriptorCCActionTable table = (DescriptorCCActionTable)
	    ctx.getRequest().getAttribute(TABLE);
        if (table == null) {
            return false;
	}
        DefaultModel model = (DefaultModel)table.getModel();
	try {
            int size = model.getSize();
            if (size > 0) {
                handlerCtx.setOutputValue("hasResults", "true");
                return true;
            } else {
                handlerCtx.setOutputValue("hasResults", "false");
                return false;
            }
	} catch (Exception ex) {
            //Log exception  ??
            return false;
	}
    }


    /**
     *	This handler switches the result output order.
     */
    public void switchDateSort(RequestContext ctx, HandlerContext handlerCtx) {
	HttpSession session = ctx.getRequest().getSession();

	// Get the existing value.
	Boolean dir = (Boolean)handlerCtx.getInputValue(LOG_DATE_SORT_DIRECTION);
	if (dir == null) {
	    dir = TRUE;
	} else {
	    dir = new Boolean(dir.booleanValue()^true);
	}
	handlerCtx.setOutputValue(SWITCH_DATE_SORT_RESULT, dir);

// FIXME: Add this as its own handler
	// The following makes sure the triangle points the right direction
	// NOTE: We don't call LH's setSort() method b/c it is private,
	//	 however, the following does the same thing.  Note, that we
	//	 only have one sortable column.  If we have more in the
	//	 future, the following line of code will not be sufficient.
	//	 Also, if LH's implementation changes this may break.
	ContainerView cv = (ContainerView)handlerCtx.getView().getParent();
	HttpServletRequest request = ctx.getRequest();
	cv.setDisplayFieldValue(
	    DescriptorCCActionTable.CHILD_PRIMARY_SORT_ORDER_HIDDEN_FIELD,
	    request.getParameter("sortOrder"));
	cv.setDisplayFieldValue(
	    DescriptorCCActionTable.CHILD_PRIMARY_SORT_NAME_HIDDEN_FIELD,
	    request.getParameter("sortName"));
    }

    public void testRunning(RequestContext ctx, HandlerContext handlerCtx) {
        View view = handlerCtx.getView();
        ServletRequest request = ctx.getRequest();
        String instanceName = (String)handlerCtx.getInputValue(INSTANCE_NAME);
        if (isServerRunning(instanceName))
            handlerCtx.setOutputValue("isRunning", "true" );
        else
            handlerCtx.setOutputValue("isRunning", "false" );
    }

    public void getLogFilesDirectory(RequestContext ctx, HandlerContext handlerCtx) {
        ServletRequest request = ctx.getRequest();
        String instanceName = (String)handlerCtx.getInputValue("instanceName");
        try {
            String dir = getLogFilesDirectory(instanceName);
            handlerCtx.setOutputValue("logFileDirectory", dir);
        }catch (Exception ex){
            handlerCtx.setOutputValue("logFileDirectory", "");
        }
    }

    public void displayAdvancedSearchOptions(RequestContext ctx, HandlerContext handlerCtx) {
	View view = handlerCtx.getView();
	if (!(view instanceof DescriptorContainerView)) {
            View parent = view.getParent();
            if (!(parent instanceof DescriptorContainerView)) {
		throw new FrameworkException("View is not a DescriptorContainerView!", null, view);
            }  else {
                view = parent;
            }
        }
        if (view instanceof DescriptorCCPageTitle) {
            view = view.getParent();
        }
	DescriptorContainerView descView = (DescriptorContainerView)view;
        ViewDescriptor propertySheetDescriptor = descView.getViewDescriptor();

	if (propertySheetDescriptor == null) {
	    throw new FrameworkException("propertySheetDescriptor is null", propertySheetDescriptor, view);
	}
	if(!(propertySheetDescriptor instanceof CCPropertySheetDescriptor)) {
	    throw new FrameworkException("propertySheetDescriptor is of wrong type", propertySheetDescriptor, view);
	}
        CCPropertySheetModelInterface model = ((CCPropertySheetDescriptor)propertySheetDescriptor).getModel();
        boolean searchAdvance = new Boolean(""+ctx.getRequest().getAttribute("ShowAdvanceSearch")).booleanValue();
	if (searchAdvance) {
	    model.setVisible("advanceSearch", true);
            model.setVisible("advanceLink", false);
            model.setVisible("basicLink", true);
            handlerCtx.setOutputValue("showAdvance", "true");
	} else {
	    model.setVisible("advanceSearch", false);
            model.setVisible("advanceLink", true);
            model.setVisible("basicLink", false);
            handlerCtx.setOutputValue("showAdvance", "false");
        }
     }
   public String setEndDisplayString(RequestContext ctx, HandlerContext handlerCtx) {
       // Get the input parameter
       String endDisplayString = (String)handlerCtx.getInputValue(END_DISPLAY_STRING);
       if (endDisplayString == null) {
           throw new FrameworkException("'"+END_DISPLAY_STRING+"' cannot be null!");
       }
                                                                                                                                                          
       // Return the value
       return endDisplayString;
   } 
   
   
    private static boolean isEmpty(String test) {
        return ((test == null) || "".equals(test));
    }

     public static String getLogFilesDirectory(String instanceName){
        if (isEmpty(instanceName))
            return "";
        String dir = "";
        try{
            if (MBeanUtil.isValidMBean("com.sun.appserv:name=logmanager,category=runtime,server="+instanceName)){
                dir = (String) MBeanUtil.invoke(
                "com.sun.appserv:name=logmanager,category=runtime,server="+instanceName,
                "getLogFilesDirectory",
                null,
                null);
            }else{
                //go through node agent proxy
                Class provider = Class.forName("com.sun.enterprise.ee.tools.admingui.handlers.NodeAgentLogProvider");
                Class[] type = new Class[]{ String.class };
                Method method = provider.getMethod("getLogFilesDirectory", type);
                Object[] args = new Object[] {instanceName};
                dir = (String) method.invoke(null, args);
            }
        }catch(Exception ex){
        }
        return dir;
     }

     public static boolean isServerRunning(String instanceName){
         if(isEmpty(instanceName))
             return false;
         return MBeanUtil.isValidMBean("com.sun.appserv:name=logmanager,category=runtime,server="+instanceName);
     }


     private AttributeList getLogRecordsUsingQuery(String logManagerObjectName, String instanceName, Object[]params, String[] QUERY_SIGNATURE)
     {
         AttributeList results = null;
         if (MBeanUtil.isValidMBean(logManagerObjectName)){
	     results = (AttributeList)MBeanUtil.invoke(
		logManagerObjectName,
		"getLogRecordsUsingQuery",
		params,
		QUERY_SIGNATURE);
            return results;
         }
         try {
             //go through node agent proxy
             Class provider = Class.forName("com.sun.enterprise.ee.tools.admingui.handlers.NodeAgentLogProvider");
             Class[] type = new Class[]{
                            String.class,
                            Object[].class};
            Method method = provider.getMethod("getLogRecordsUsingQuery", type);
            Object[] args = new Object[] { instanceName, params};
            results = (AttributeList) method.invoke(null, args);
         }catch (Exception ex){
            throw new FrameworkException (ex);
         }
         return results;
     }


    /**
     *
     */
    public static final Integer ONE = new Integer(1);

    /**
     *	FALSE
     */
    public static final Boolean FALSE = new Boolean(false);

    /**
     *	TRUE
     */
    public static final Boolean TRUE = new Boolean(true);

    /**
     *	This is the log query method signature.
     */
    public static final String[] QUERY_SIGNATURE = {
            "java.lang.String",         //Added the extra parameter to bring up the page. Need to revisit.
	    "java.lang.Long",		// fromRecord
	    "java.lang.Boolean",	// next?
	    "java.lang.Boolean",	// forward?
	    "java.lang.Integer",	// requestedCount
	    "java.util.Date",		// fromDate
	    "java.util.Date",		// toDate
	    "java.lang.String",		// logLevel
	    "java.lang.Boolean",	// onlyLevel?
	    "java.util.List",		// listOfModules
	    "java.util.Properties"	// nameValueMap
	};


    /**
     *	Attribute Input "logTable", the table used to display the output
     */
    public static final String TABLE = "logTable";

    /**
     *	Attribute Input "logModel", the model to use
     */
    public static final String LOG_MODEL = "logModel";

    /**
     *	Attribute Input "dateEnabled"
     */
    public static final String DATE_ENABLED = "dateEnabled";

    /**
     *	Attribute Input "afterRecord"
     */
    public static final String AFTER_RECORD = "afterRecord";

    /**
     *	Attribute Input "fromRecord"
     */
    public static final String FROM_RECORD = "fromRecord";

    /**
     *	Attribute Input "archivedLogFile"
     */
    public static final String ARCHIVED_LOG_FILE = "archivedLogFile";


    /**
     *	Attribute Input "fromDate"
     */
    public static final String FROM_DATE = "fromDate";

    /**
     *	Attribute Input "fromTime"
     */
    public static final String FROM_TIME = "fromTime";

    /**
     *	Attribute Input "toDate"
     */
    public static final String TO_DATE = "toDate";

    /**
     *	Attribute Input "toTime"
     */
    public static final String TO_TIME = "toTime";

    /**
     *	Attribute Input "loggers"
     */
    public static final String LOGGERS = "loggers";

    /**
     *	Attribute Input "logLevel"
     */
    public static final String LOG_LEVEL = "logLevel";

    /**
     *	Attribute Input "customLogger";
     */
    public static final String CUSTOM_LOGGERS = "customLogger";

    /**
     *	Attribute Input "nvp";
     */
    public static final String NVP = "nvp";

    /**
     *	Attribute Input "numberToDisplay";
     */
    public static final String NUMBER_TO_DISPLAY = "numberToDisplay";

    /**
     *	Attribute Input "only";
     */
    public static final String ONLY_LEVEL = "only";

    /**
     *	This is a Session key to the sort direction.
     */
    public static final String LOG_DATE_SORT_DIRECTION = "LOG_DATE_SORT_DIRECTION";
    public static final String SWITCH_DATE_SORT_RESULT	= "result";

    /**
     *
     */
    public static final String FIRST_LOG_ROW = "firstLogRow";

    /**
     *
     */
    public static final String LAST_LOG_ROW = "lastLogRow";

    /**
     *	If the number to display is not specified, this value will be used
     *	(40).
     */
    public static final Integer DEFAULT_NUMBER_TO_DISPLAY = new Integer(40);

    /**
     *	The following constant defines the valid delimiters that can be used
     *	to seperate custom loggers on input. (" \t\n\r\f,;:")
     */
    public static final String CUSTOM_LOGGER_DELIMITERS = " \t\n\r\f,;:";

    /**
     *	The following constant defines the valid delimiters that can be used
     *	to seperate nvp entries on input. (" \t\n\r\f,;:")
     */
    public static final String NVP_DELIMITERS = " \t\n\r\f,;:";

    /**
     *	This is the delimiter between the property name and property value.
     */
    public static final char EQUALS = '=';

    /**
     *	This specifies how TIME fields are input and displayed.  We need to do
     *	this in order to get a display/input that works with milliseconds.
     *	Perhaps in the future we may want to just append the milliseconds?
     */
    public static final String TIME_FORMAT = " HH:mm:ss.SSS";
    public static final String TIME_FORMAT_2 = " HH:mm:ss";
    public static final String TIME_FORMAT_3 = " HH:mm";

    /**
     *	This defines the output parameter for the getTime handler. ("value")
     */
    public static final String GET_TIME_RESULT = "value";

    /**
     *	This specifies the attribute used to define the date format (short,
     *	medium, long, full). ("dateFormat")
     */
    public static final String DATE_FORMAT = "dateFormat";

    /**
     *	This specifies the output value used to return the date. ("value")
     */
    public static final String DATE_VALUE = "value";

    /**
     *	This defines the short date format, used by DATE_FORMAT. ("short")
     */
    public static final String GET_DATE_SHORT = "short";

    /**
     *	This defines the medium date format, used by DATE_FORMAT. ("medium")
     */
    public static final String GET_DATE_MEDIUM = "medium";

    /**
     *	This defines the long date format, used by DATE_FORMAT. ("long")
     */
    public static final String GET_DATE_LONG = "long";

    /**
     *	This defines the full date format, used by DATE_FORMAT. ("full")
     */
    public static final String GET_DATE_FULL = "full";

    /**
     *	This attribute key should have a value of "false" in order to retrieve
     *	the full messages.
     */
    public static final String TRUNCATE_MESSAGE = "truncateMessage";

    /**
     *	This attribute is used if TRUNCATE_MESSAGE is true.  The attribute
     *	associated with this key should be a number representing the number of
     *	characters shown before the message is truncated.
     */
    public static final String TRUNCATE_LENGTH = "truncateLength";

    /**
     *	This model key is set by the filter method, it is true if a
     *	level image should be displayed.
     */
    public static final String SHOW_LEVEL_IMAGE = "showLevelImage";

    /**
     *	This is the root directory of the alert images
     */
    public static final String LEVEL_IMAGE_ROOT =
	"/com_sun_web_ui/images/alerts/";

    /**
     *	Input parameter to specify the resource bundle.
     */
    public static final String RESOURCE_BUNDLE = "resourceBundle";

    /**
     *	The input value key for the String representing the previous button.
     */
    public static final String PREV_BUTTON_STRING = "prevButtonString";

    /**
     *	The input value key for the String representing the next button.
     */
    public static final String NEXT_BUTTON_STRING = "nextButtonString";
    
    /**
     *	The input value key for the String representing search links.
     */
    public static final String END_DISPLAY_STRING = "endDisplayString";

    /**
     *	Input parameter to specify the server instance name.
     */
    public static final String LOG_MANAGER_OBJECT_NAME = "logManagerObjectName";
    public static final String INSTANCE_NAME = "instanceName";
}
