/*
 * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/filter/LogFilter.java,v 1.14 2005/01/25 10:41:40 luetzkendorf Exp $
 * $Revision: 1.14 $
 * $Date: 2005/01/25 10:41:40 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.webdav.filter;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Principal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.slide.webdav.logger.StatusHttpServletResponseWrapper;
import org.apache.slide.webdav.util.WebdavStatus;

/**
 * A servlet filter for one-line-per-request logging. Log format and where to
 * output can be configured in the deployment descriptors.
 *
 * @version $Revision: 1.14 $
 */
public class LogFilter implements Filter {

    FilterConfig config;
    ServletContext context;
    String logFormat = "%T, %t, %P, %m, %s \"%l\", %i, %p";
    boolean outputToConsole = true;
    boolean outputToServletLog = false;
    boolean outputToFile = false;
    String outputFilePath = null;
    File outputFile = null;
    BufferedOutputStream fout = null;
    DateFormat df;
    String dateTimePattern = "dd-MMM-yyyy HH:mm:ss";

    // log elements

    /**
     * Interface implementation
     *
     * @param    config              a  FilterConfig
     *
     * @throws   ServletException
     *
     */
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
        this.context = config.getServletContext();
        this.df = new SimpleDateFormat( dateTimePattern );

        // get the init parms
        String p;
        p = config.getInitParameter( "logFormat" );
        if( p != null && !"".equals(p) )
            logFormat = p;
        p = config.getInitParameter( "outputToConsole" );
        if( "false".equalsIgnoreCase(p) )
            outputToConsole = false;
        p = config.getInitParameter( "outputToServletLog" );
        if( "true".equalsIgnoreCase(p) )
            outputToServletLog = true;
        p = config.getInitParameter( "outputToFile" );
        if( p != null && !"".equals(p) ) {
            outputFilePath = p;
            outputFile = new File( outputFilePath );
            try {
                if( outputFile.canWrite() || outputFile.createNewFile() ) {
                    fout = new BufferedOutputStream( new FileOutputStream(outputFilePath, true) );
                    outputToFile = true;
                }
            }
            catch (IOException e) {}
        }
    }

    /**
     * Interface implementation
     *
     * @param    req                 a  ServletRequest
     * @param    resp                a  ServletResponse
     * @param    chain               a  FilterChain
     *
     * @throws   IOException
     * @throws   ServletException
     *
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        StatusHttpServletResponseWrapper resp = new StatusHttpServletResponseWrapper((HttpServletResponse)response);
        long start = System.currentTimeMillis();

        // incomming
        String thread = Thread.currentThread().getName();
        String useragent = req.getHeader("User-Agent") != null
            ? req.getHeader("User-Agent")
            : "<user-agent-unknown>";
        String datetime = df.format( new Date() );
        String method = req.getMethod();
        String uri = req.getRequestURI();
        Principal p = req.getUserPrincipal();
        String principal = (p != null ? p.getName() : null); 
        // with tomcat p.getName() may be null too
        if (principal == null) principal = "unauthenticated";
        String contentlength = req.getHeader( "Content-Length" );
        if( contentlength == null )
            contentlength = "-";

        String transaction = req.getHeader("Transaction");
        if (transaction == null && req.getMethod().equals("UNLOCK")) {
            transaction = req.getHeader("Lock-Token");
        }
        if (transaction == null) transaction = "-";
        
        // next please!
        chain.doFilter( req, resp );

        // way back
        int status = resp.getStatus();
        String message = WebdavStatus.getStatusText(status);
        String detail = resp.getStatusText();
        if( detail == null || "".equals(detail) )
            detail = message;
        String path = (String)req.getAttribute("slide_uri"); // set by

        long end = System.currentTimeMillis();
        logLine( (end-start), status, thread, method, datetime, uri, path, contentlength, principal,
               message, detail, useragent, transaction);
    }

    /**
     * Log one line.
     *
     * @param    req                 a  XHttpServletRequestFacade
     * @param    resp                a  XHttpServletResponseFacade
     * @param    elapsed             a  long
     *
     * @throws   IOException
     *
     */
    private void logLine(long elapsed, int status,
                        String thread, String method, String datetime, String uri,
                         String path, String contentlength, String principal,
                        String message, String detail, String useragent, String transaction)
        throws IOException
    {

        StringBuffer b = new StringBuffer( logFormat );
        int i;
        i = b.toString().indexOf("%T");
        if( i >= 0 ) b.replace( i, i+2, thread );
        i = b.toString().indexOf("%t");
        if( i >= 0 ) b.replace( i, i+2, datetime );
        i = b.toString().indexOf("%P");
        if( i >= 0 ) b.replace( i, i+2, principal );
        i = b.toString().indexOf("%m");
        if( i >= 0 ) b.replace( i, i+2, method );
        i = b.toString().indexOf("%s");
        if( i >= 0 ) b.replace( i, i+2, String.valueOf(status) );
        i = b.toString().indexOf("%l");
        if( i >= 0 ) b.replace( i, i+2, message );
        i = b.toString().indexOf("%L");
        if( i >= 0 ) b.replace( i, i+2, detail );
        i = b.toString().indexOf("%i");
        if( i >= 0 ) b.replace( i, i+2, String.valueOf(elapsed)+" ms" );
        i = b.toString().indexOf("%p");
        if( i >= 0 ) b.replace( i, i+2, path );
        i = b.toString().indexOf("%u");
        if( i >= 0 ) b.replace( i, i+2, uri );
        i = b.toString().indexOf("%x");
        if( i >= 0 ) b.replace( i, i+2, contentlength );
        i = b.toString().indexOf("%A");
        if( i >= 0 ) b.replace( i, i+2, useragent );
        i = b.toString().indexOf("%n");
        if( i >= 0 ) b.replace( i, i+2, transaction );

        if( outputToConsole )
            System.out.println( b.toString() );
        if( outputToServletLog )
            context.log( b.toString() );
        if( outputToFile ) {
            b.append("\n");
            fout.write( b.toString().getBytes("UTF-8") );
            fout.flush();
        }
    }

    /**
     * Interface implementation.
     *
     */
    public void destroy() {
        try {
            if( outputToFile )
                fout.close();
        }
        catch (IOException e) {}
    }
}

