/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *     "Apache Jetspeed" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache" or
 *    "Apache Jetspeed", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.jetspeed.services.portletstats;

// turbine stuff
import org.apache.turbine.util.RunData;
import org.apache.turbine.services.Service;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.resources.ResourceService;
import org.apache.turbine.util.Log;

// jetspeed stuff
import org.apache.jetspeed.portal.Portlet;

// javax stuff
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;

// java stuff
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Simple implementation of the PortletStatsService. This implementation
 * uses <A HREF="http://httpd.apache.org/docs/logs.html">Apache Common Log Format (CLF)</A> as its default log format.
 * This format uses the following pattern string: "%h %l %u %t \"%r\" %>s %b",
 * where:
 * <UL>
 * <LI><B>%h</B> - remote host</LI>
 * <LI><B>%l</B> - remote log name</LI>
 * <LI><B>%u</B> - remote user</LI>
 * <LI><B>%t</B> - time in common log time format</LI>
 * <LI><B>%r</B> - first line of request</LI>
 * <LI><B>%s</B> - status (either 200 or 401)</LI>
 * <LI><B>%b</B> - bytes sent (always "-" for no bytes sent). Optionally, portlet load time may be logged (see logLoadTime property)</LI>
 * </UL>
 * <P>
 * Here's an example log entry:
 * <P>
 * <CODE>127.0.0.1 - turbine [26/Aug/2002:11:44:40 -0500] "GET /jetspeed/DatabaseBrowserTest HTTP/1.1" 200 -</CODE>
 * <P>
 * TODO:
 * <UL>
 * <LI>Statistics cache (by portlet and by user)</LI>
 * <LI>Portlet exclusion</LI>
 * <LI>Configurable format pattern</LI>
 * </UL>
 * 
 * @author <a href="mailto:morciuch@apache.org">Mark Orciuch</a>
 * @version $Id: JetspeedPortletStatsService.java,v 1.2 2003/03/13 01:08:11 morciuch Exp $
 */
public class JetspeedPortletStatsService extends TurbineBaseService
    implements PortletStatsService
{

    /**
     * The name of logger to use. If log4j is used, the logger can be
     * configured with variety options including rolling appender.
     */
    private String loggerName = null;
            
    /**
     * The default log format pattern string to use with the following elements:
     * <OL START="0">
     * <LI>remote address</LI>
     * <LI>always "-"</LI>
     * <LI>user name</LI>
     * <LI>timestamp</LI>
     * <LI>request method</LI>
     * <LI>context</LI>     
     * <LI>portlet name</LI>
     * <LI>request protocol</LI>
     * <LI>status code</LI>
     * <LI>always "-" unless logLoadTime is true</LI>
     * </OL>
     */
    private static final String defaultLogFormat = "{0} {1} {2} [{3}] \"{4} {5}/{6} {7}\" {8} {9}";

    /**
     * Logging enabled flag. If TRUE, the logging will occur. To improve performance,
     * the application should use isEnabled() method before calling logAccess().
     */
    private boolean enabled = false;

    /**
     * Date format to use in the log entry. Should conform to standard
     * format used by the SimpleDateFormat class.
     */
    private String dateFormat = null;

    /** Date formatter */
    private SimpleDateFormat formatter = null;

    /** Log portlet load time instead of bytes sent (which is always zero) */
    private boolean logLoadTime = false;

    /**
     * This is the early initialization method called by the 
     * Turbine <code>Service</code> framework
     */
    public void init( ServletConfig conf ) throws InitializationException
    {

        ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
                                                     .getResources(PortletStatsService.SERVICE_NAME);

        this.loggerName = serviceConf.getString("logger", "access");
        this.enabled = serviceConf.getBoolean("enabled");
        this.dateFormat = serviceConf.getString("dateFormat", "dd/MM/yyyy:hh:mm:ss z");
        this.formatter = new SimpleDateFormat(this.dateFormat);
        this.logLoadTime = serviceConf.getBoolean("logLoadTime", false);
        
        setInit(true);

    }
            
    /**
     * @see org.apache.jetspeed.services.portletstats.PortletStatsService#isEnabled
     */
    public boolean isEnabled()
    {
        return this.enabled;
    }

    /**
     * @see org.apache.jetspeed.services.portletstats.PortletStatsService#setEnabled
     */
    public boolean setEnabled(boolean state)
    {
        boolean oldState = this.enabled;
        this.enabled = state;

        return oldState;
    }

    /**
     * @see org.apache.jetspeed.services.portletstats.PortletStatsService#logAccess
     */
    public void logAccess(RunData data, Portlet portlet, String statusCode, long time)
    {
        
        if (!this.isEnabled())
        {
            return;
        }
        
        try 
        {
            Log.info(loggerName, this.getLogMessage(data, portlet, statusCode, time));
        }
        catch (Exception e)
        {
            Log.error(e);
        }
    }

    /**
     * Formats log message
     * 
     * @param data
     * @param portlet
     * @param statusCode
     * @param time
     * @return Formatted message
     * @exception Exception
     */
    private String getLogMessage(RunData data, Portlet portlet, String statusCode, long time) 
    throws Exception
    {        
 
        HttpServletRequest req = data.getRequest();
        Object[] args = {
            req.getRemoteAddr(),
            "-",
            data.getUser().getUserName(),
            this.formatter.format(new Date()),
            req.getMethod(),
            req.getContextPath(),
            portlet.getName(),
            req.getProtocol(),
            statusCode,
            this.logLoadTime == true ? String.valueOf(time) : "-"
        }; 

        return MessageFormat.format(defaultLogFormat, args).toString();

    }

    /**
     * Formats log message using default load time
     * 
     * @param data
     * @param portlet
     * @param statusCode
     */
    public void logAccess(RunData data, Portlet portlet, String statusCode) 
    {
        logAccess(data, portlet, statusCode, 0);
    }
    
}

