/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software
 * License version 1.1, a copy of which has been included with this
 * distribution in the LICENSE.APL file.  */

package org.apache.log4j.net;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Priority;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.TriggeringEventEvaluator;
import org.apache.log4j.spi.LocationInfo;

import java.net.Socket;
import java.net.UnknownHostException;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.net.MalformedURLException;
import java.io.IOException;
import java.util.Date;
/**
   Send an http GET when logging event occurs, typically on
   want to do this on errors or fatal errors.

   <p>Nore: This uses a GET. There might be some issues with the 
   size of the incoming messager that the Web server can handle 
   if the stack trace is large.
   
   @author Adrian Blakey - R-Objects Inc.
   @since 1.0 */

public class HTTPAppender extends AppenderSkeleton {

    // Default property values. These are the names of the
    // N/V pairs used to send the corresponding value
    static final String CLASSNAME = "className";
    static final String FILENAME = "fileName";
    static final String LINENUMBER = "lineNumber";
    static final String METHODNAME = "methodName";
    static final String MSG = "msg";
    static final String NDC = "ndc";
    static final String STARTTIME = "startTime";
    static final String THREADNAME = "threadName";
    static final String STACKTRACE = "stackTrace";

    // Properties
    protected String logURL;
    protected String className;
    protected String fileName;
    protected String lineNumber;
    protected String methodName;
    protected String msg;
    protected String ndc;
    protected String startTime;
    protected String threadName;
    protected String stackTrace;

    protected URL url;
    protected int port = 0;
    protected String host;

    protected void append(LoggingEvent event) {
	if (url == null) {
	    LogLog.error("Missing property -- log4j.appender.xxx.logURL");
	    return;
	}
	if (! url.getProtocol().equals("http") ) {
	    LogLog.error("Only the http protocol is supported by this appender. Change the value of: log4j.appender.xxx.logURL");
	    return;

	}
	sendRequest(event);                    // Send it
    }
    
    public void close() {
	// nop
    }

    public boolean requiresLayout() {
        // We do not need a layout -- we have to make a http message
        return false;
    }

    public void activateOptions() {
	if (logURL != null) {
	    try {
            url = new URL(logURL);
            port = url.getPort();
            host = url.getHost();             // For later
	    }
	    catch (MalformedURLException e) {
            // Do not do anything for now -- we catch it in the append
            LogLog.error("Invalid URL assigned to property: log4j.appender.xxx.logURL");
	    }
	}
	else {
	    LogLog.error("Missing property: log4j.appender.xxx.logURL");
	}
	if (className == null) {
	    className = CLASSNAME;
	}
	if (fileName == null) {
	    fileName = FILENAME;
	}
	if (lineNumber == null) {
	    lineNumber = LINENUMBER;
	}
	if (methodName == null) {
	    methodName = METHODNAME;
	}
	if (msg == null) {
	    msg = MSG;
	}
	if (ndc == null) {
	    ndc = NDC;
	}
	if (startTime == null) {
	    startTime = STARTTIME;
	}
	if (threadName == null) {
	    threadName = THREADNAME;
	}
	if (stackTrace == null) {
	    stackTrace = STACKTRACE;
	}
    }
    
    /**
     * Set the logURL property
     **/
    public void setLogURL(String logURL) {
        this.logURL = logURL;
    }
    
    public String getLogURL() {
        return logURL;
    }
    /**
     * Set the className property
     **/
    public void setClassName(String className) {
        this.className = className;
    }
    
    public String getClassName() {
        return className;
    }
    /**
     * Set the fileName property
     **/
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
    
    public String getFileName() {
        return fileName;
    }
    /**
     * Set the lineNumber property
     **/
    public void setLineNumber(String lineNumber) {
        this.lineNumber = lineNumber;
    }
    
    public String getLineNumber() {
        return lineNumber;
    }
    
    /**
     * Set the methodName
     **/
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    
    public String getMethodName() {
        return methodName;
    }
    
    /**
     * Set the msg property
     **/
    public void setMsg(String msg) {
        this.msg = msg;
    }
    
    public String getMsg() {
        return msg;
    }
    /**
     * Set the ndc property
     **/
    public void setNdc(String ndc) {
        this.ndc = ndc;
    }

    public String getNdc() {
        return ndc;
    }
    /**
     * Set the startTime property
     **/
    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }

    public String getStartTime() {
        return startTime;
    }

    /**
     * Set the threadName property
     **/
    public void setThreadName(String threadName) {
        this.threadName = threadName;
    }

    public String getThreadName() {
        return threadName;
    }
    /**
     * Set the stackTrace property
     **/
    public void setStackTrace(String stackTrace) {
        this.stackTrace = stackTrace;
    }

    public String getStackTrace() {
        return stackTrace;
    }

    /**
     * Helper to see if we have a network connection to the httpd
     **/
    private boolean deadPort() {
        if (port <= 0) { port = 80; }
        try {
            Socket s = new Socket(host, port);
        } catch (UnknownHostException uhe) {
            return true;
        } catch ( IOException ioe ) {
            return true;
        }
        return false;
    }

    protected void sendRequest(LoggingEvent event) {
        // This does all the heavy lifting of sending the error
        // over http
        // FIXME: start a new thread?
        // Make nice N/V pairs from the event, we will do our own layout
        if ( event == null || deadPort()) {
            LogLog.error( "Error not logged to http host - missing network connection" );
            return;
        }
        StringBuffer sbuf = new StringBuffer();
        LocationInfo li = event.getLocationInformation();
        Date st = new Date(event.getStartTime());
        sbuf.append(logURL+"?"+className+"="+li.getClassName()+
                    "&"+fileName+"="+li.getFileName()+
                    "&"+lineNumber+"="+li.getLineNumber()+
                    "&"+methodName+"="+li.getMethodName()+
                    "&"+ndc+"="+event.getNDC()+
                    "&"+startTime+"="+URLEncoder.encode(st.toString())+
                    "&"+threadName+"="+event.getThreadName() );
        String msgValue = event.getRenderedMessage();
        if (msg != null) {
            sbuf.append("&"+msg+"="+URLEncoder.encode(msgValue));
        }
        String[] s = event.getThrowableStrRep();
        if (s != null) {
            StringBuffer stbuf = new StringBuffer();
            sbuf.append("&"+stackTrace+"=");
            for(int j = 0; j < s.length; j++) {
                stbuf.append(s[j]);
            }
            sbuf.append(URLEncoder.encode(new String(stbuf)));
        }
        String theUrl = null;
        try {
            theUrl = new String(sbuf);
            url = new URL(theUrl);
            HttpURLConnection uc = null;
            int rc = 0;
            try {
                uc = (HttpURLConnection)url.openConnection();
                uc.connect();
                rc = uc.getResponseCode();
                if (rc == HttpURLConnection.HTTP_OK) {
                }
                else {
                    LogLog.error( "Error: Server URL: " + theUrl + " returned: " + rc + " Response msg: " + uc.getResponseMessage() ); 
                }
             }
            catch (IOException e) {
                LogLog.error( "Error sending log request to server: " + host);
            }
            finally {
                if (uc != null) uc.disconnect();
            }
        }
        catch (MalformedURLException e) {
            LogLog.error("Malformed URL " + theUrl);
            // Do not do anything for now -- we catch it in the append
        }
    }
}


