PerformanceInterceptorServiceImpl.java

package org.apache.fulcrum.yaafi.interceptor.performance;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.lang.reflect.Method;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.Reconfigurable;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext;
import org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl;
import org.apache.fulcrum.yaafi.interceptor.util.ArgumentToStringBuilderImpl;
import org.apache.fulcrum.yaafi.interceptor.util.MethodToStringBuilderImpl;
import org.apache.fulcrum.yaafi.interceptor.util.StopWatch;

/**
 * A service logging the execution time of service invocations.
 *
 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
 */

public class PerformanceInterceptorServiceImpl extends BaseInterceptorServiceImpl
		implements PerformanceInterceptorService, Reconfigurable, Contextualizable, ThreadSafe {
	/** the maximum length of a dumped argument */
	private static final int MAX_ARG_LENGTH = 100;

	/** default length of the StringBuilder */
	private static final int BUFFER_LENGTH = 2000;

	/** seperator for the arguments in the logfile */
	private static final String SEPERATOR = ";";

	/** the tresholds in milliseconds to determine the loglevel */
	private int[] tresholdList;

	/** maximum argument length for dumping arguments */
	private int maxArgLength;

	/////////////////////////////////////////////////////////////////////////
	// Avalon Service Lifecycle Implementation
	/////////////////////////////////////////////////////////////////////////

	/**
	 * Constructor
	 */
	public PerformanceInterceptorServiceImpl() {
		super();
		this.tresholdList = new int[5];
	}

	/**
	 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
	 */
	public void configure(Configuration configuration) throws ConfigurationException {
		super.configure(configuration);

		this.maxArgLength = configuration.getChild("maxArgLength").getValueAsInteger(MAX_ARG_LENGTH);
		Configuration tresholdConfiguration = configuration.getChild("tresholds");
		this.tresholdList[0] = tresholdConfiguration.getChild("fatal").getAttributeAsInteger("millis", 5000);
		this.tresholdList[1] = tresholdConfiguration.getChild("error").getAttributeAsInteger("millis", 1000);
		this.tresholdList[2] = tresholdConfiguration.getChild("warn").getAttributeAsInteger("millis", 500);
		this.tresholdList[3] = tresholdConfiguration.getChild("info").getAttributeAsInteger("millis", 100);
		this.tresholdList[4] = tresholdConfiguration.getChild("debug").getAttributeAsInteger("millis", 10);
	}

	/**
	 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
	 */
	public void reconfigure(Configuration configuration) throws ConfigurationException {
		super.reconfigure(configuration);
		this.configure(configuration);
	}

	/////////////////////////////////////////////////////////////////////////
	// Service interface implementation
	/////////////////////////////////////////////////////////////////////////

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#
	 * onEntry(org.apache.fulcrum.yaafi.framework.interceptor.
	 * AvalonInterceptorContext)
	 */
	public void onEntry(AvalonInterceptorContext interceptorContext) {
		if (this.isServiceMonitored(interceptorContext)) {
			this.createStopWatch(interceptorContext);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#
	 * onError(org.apache.fulcrum.yaafi.framework.interceptor.
	 * AvalonInterceptorContext, java.lang.Throwable)
	 */
	public void onError(AvalonInterceptorContext interceptorContext, Throwable t) {
		if (this.isServiceMonitored(interceptorContext)) {
			StopWatch stopWatch = this.getStopWatch(interceptorContext);
			stopWatch.stop();
			this.log(ON_ERROR, interceptorContext, stopWatch);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl#
	 * onExit(org.apache.fulcrum.yaafi.framework.interceptor.
	 * AvalonInterceptorContext, java.lang.Object)
	 */
	public void onExit(AvalonInterceptorContext interceptorContext, Object result) {
		if (this.isServiceMonitored(interceptorContext)) {
			StopWatch stopWatch = this.getStopWatch(interceptorContext);
			stopWatch.stop();
			this.log(ON_EXIT, interceptorContext, stopWatch);
		}
	}

	/////////////////////////////////////////////////////////////////////////
	// Service Implementation
	/////////////////////////////////////////////////////////////////////////

	/**
	 * Creates a stop watch
	 *
	 * @param interceptorContext the current interceptor context
	 */
	protected void createStopWatch(AvalonInterceptorContext interceptorContext) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		interceptorContext.getRequestContext().put(this.getServiceName(), stopWatch);
	}

	/**
	 * Gets the stop watch
	 *
	 * @param interceptorContext the current interceptor context
	 * @return the stop watch
	 */
	protected StopWatch getStopWatch(AvalonInterceptorContext interceptorContext) {
		return (StopWatch) interceptorContext.getRequestContext().remove(this.getServiceName());
	}

	/**
	 * Logs the execution time.
	 *
	 * @param mode               the invocation mode (onEntry, onExit, onError)
	 * @param interceptorContext the current interceptor context
	 * @param stopWatch          the stop watch
	 */
	protected void log(int mode, AvalonInterceptorContext interceptorContext, StopWatch stopWatch) {
		String msg = null;
		long time = stopWatch.getTime();

		if (time >= tresholdList[0]) {
			if (this.getLogger().isFatalErrorEnabled()) {
				msg = this.toString(interceptorContext, stopWatch, mode);
				this.getLogger().fatalError(msg);
			}
		} else if (time >= tresholdList[1]) {
			if (this.getLogger().isErrorEnabled()) {
				msg = this.toString(interceptorContext, stopWatch, mode);
				this.getLogger().error(msg);
			}
		} else if (time >= tresholdList[2]) {
			if (this.getLogger().isWarnEnabled()) {
				msg = this.toString(interceptorContext, stopWatch, mode);
				this.getLogger().warn(msg);
			}
		} else if (time >= tresholdList[3]) {
			if (this.getLogger().isInfoEnabled()) {
				msg = this.toString(interceptorContext, stopWatch, mode);
				this.getLogger().info(msg);
			}
		} else if (time >= tresholdList[4] && this.getLogger().isDebugEnabled() == true) {
			msg = this.toString(interceptorContext, stopWatch, mode);
			this.getLogger().debug(msg);
		}
	}

	/**
	 * Create the log message for the performance logfile.
	 *
	 * @param interceptorContext the context
	 * @param stopWatch          the stopwatch
	 * @param mode               the mode (onEntry, onExit, onError)
	 * @return the log message
	 */
	protected String toString(AvalonInterceptorContext interceptorContext, StopWatch stopWatch, int mode) {
		Method method = interceptorContext.getMethod();
		Object[] args = interceptorContext.getArgs();
		MethodToStringBuilderImpl methodToStringBuilder = new MethodToStringBuilderImpl(method);
		StringBuilder result = new StringBuilder(BUFFER_LENGTH);

		result.append(interceptorContext.getTransactionId());
		result.append(SEPERATOR);
		result.append(interceptorContext.getInvocationId());
		result.append(SEPERATOR);
		result.append(interceptorContext.getInvocationDepth());
		result.append(SEPERATOR);
		result.append(mode);
		result.append(SEPERATOR);
		result.append(interceptorContext.getServiceShorthand());
		result.append(SEPERATOR);
		result.append(method.getName());
		result.append(SEPERATOR);
		result.append(stopWatch.getTime());
		result.append(SEPERATOR);
		result.append(methodToStringBuilder.toString());
		result.append(SEPERATOR);
		result.append(this.toString(args));

		return result.toString();
	}

	/**
	 * Prints the argument list.
	 *
	 * @param args array of arguments
	 * @return the debug output
	 */
	protected String toString(Object[] args) {
		StringBuilder result = new StringBuilder();
		ArgumentToStringBuilderImpl toStringBuilder = null;

		if (args == null) {
			args = new Object[0];
		}

		for (int i = 0; i < args.length; i++) {
			toStringBuilder = new ArgumentToStringBuilderImpl(args[i], this.maxArgLength, 1);
			result.append("arg[" + i + "]:={");
			result.append(toStringBuilder.toString());
			result.append("}");

			if (i < args.length - 1) {
				result.append(SEPERATOR);
			}
		}

		return result.toString();
	}

}