/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.jsfunit.framework;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
* The JSFTimer collects performance data during a JSF request. It tracks
* the time for each phase in the JSF Lifecycle and remains accurate even when
* phases are skipped.
*
* To use this class you just need to get the instance with the static getTimer()
* method. The instance is held in the session.
*
* Statistics are reset at the beginning of each JSF request. So you can use
* this class during the JSF lifecycle or afterwards in a JSFUnit test.
*
* To use this class, you must enable the JSFTimerPhaseListener in faces-config.xml.
*
* @author Stan Silvert
* @since 1.0
* @see org.jboss.jsfunit.framework.JSFTimerPhaseListener
*/
public class JSFTimer
{
public static final String REQUEST_KEY = JSFTimer.class.getName() + ".REQUEST_KEY";
private Map<PhaseId, Long> beforeMap = new HashMap<PhaseId, Long>(5);
private Map<PhaseId, Long> afterMap = new HashMap<PhaseId, Long>(5);
// only allow this class to create an instance
private JSFTimer()
{
}
// ---------------- package private methods --------------------
void beforePhase(PhaseId phaseId)
{
beforeMap.put(phaseId, new Long(System.currentTimeMillis()));
}
void afterPhase(PhaseId phaseId)
{
afterMap.put(phaseId, new Long(System.currentTimeMillis()));
}
// -------------------------------------------------------------
/**
* Get a reference to the JSFTimer.
*
* @return The JSFTimer.
*
* @throws IllegalStateException If no JSF requests have been made on this session.
*/
public static JSFTimer getTimer()
{
FacesContext facesContext = FacesContext.getCurrentInstance();
try
{
if (facesContext == null)
{
facesContext = FacesContextBridge.getCurrentInstance();
}
}
catch (NullPointerException e)
{
throw new IllegalStateException("No JSF request has been made for this session.");
}
Map requestMap = facesContext.getExternalContext().getRequestMap();
JSFTimer timer = (JSFTimer)requestMap.get(REQUEST_KEY);
if (timer == null)
{
timer = new JSFTimer();
requestMap.put(REQUEST_KEY, timer);
}
return timer;
}
/**
* Returns the total time spent in the JSF lifecycle.
*
* @return The total time in milliseconds. Returns -1 if no phase has been completed.
*/
public long getTotalTime()
{
long firstTimeStamp = Long.MAX_VALUE;
for (Iterator<Long> i = beforeMap.values().iterator(); i.hasNext();)
{
long time = i.next().longValue();
if (time < firstTimeStamp) firstTimeStamp = time;
}
long lastTimeStamp = Long.MIN_VALUE;
for (Iterator<Long> i = afterMap.values().iterator(); i.hasNext();)
{
long time = i.next().longValue();
if (time > lastTimeStamp) lastTimeStamp = time;
}
if ((firstTimeStamp == Long.MAX_VALUE) || (lastTimeStamp == Long.MIN_VALUE))
{
return -1L;
}
return lastTimeStamp - firstTimeStamp;
}
/**
* Returns the amount of time spent during a JSF phase.
*
* @param phaseId The PhaseId.
*
* @return The time in milliseconds, or zero if the phase has not run.
*
* @throws IllegalArgumentException if the phaseId is ANY_PHASE.
*/
public long getPhaseTime(PhaseId phaseId)
{
if (phaseId == PhaseId.ANY_PHASE)
{
throw new IllegalArgumentException("PhaseId.ANY_PHASE is not valid.");
}
Long beforeTime = beforeMap.get(phaseId);
Long afterTime = afterMap.get(phaseId);
if ((beforeTime == null) || (afterTime == null)) return 0L;
return afterTime.longValue() - beforeTime.longValue();
}
}