/*
* Copyright (C) 2003-2010 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2003-2010 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.jcr.statistics;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* This aspect is used to collect all the statistics of all the methods of the JCR API.
*
* Created by The eXo Platform SAS
* Author : Nicolas Filotto
* nicolas.filotto@exoplatform.com
* 26 mars 2010
*/
@Aspect
public abstract class JCRAPIAspect
{
/**
* The logger
*/
private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.statistics.JCRAPIAspectC");
/**
* The result of the mapping if the corresponding value cannot be found.
*/
private static final Statistics UNKNOWN = new Statistics(null, "?");
/**
* The flag that indicates if the aspect is initialized or not.
*/
private static volatile boolean INITIALIZED;
/**
* The list of all the interfaces for which we want statistics
*/
private static Class<?>[] TARGET_INTERFACES;
/**
* The mapping between the AspectJ signature and the target statistics
*/
private static volatile Map<String, Statistics> MAPPING = Collections.unmodifiableMap(new HashMap<String, Statistics>());
/**
* The list of all the statistics, one per method
*/
private final static Map<String, Map<String, Statistics>> ALL_STATISTICS = new HashMap<String, Map<String, Statistics>>();
/**
* Gives the name of the statistics from the given {@link Method}
*/
private static String getStatisticsName(Method m)
{
StringBuilder sb = new StringBuilder();
sb.append(m.getName());
sb.append('(');
Class<?>[] types = m.getParameterTypes();
if (types != null)
{
boolean first = true;
for (Class<?> type : types)
{
if (first)
{
first = false;
}
else
{
sb.append(", ");
}
sb.append(type.getSimpleName());
}
}
sb.append(')');
return sb.toString();
}
/**
* Gives the corresponding statistics for the given target class and AspectJ signature
* @param target the target {@link Class}
* @param signature the AspectJ signature
* @return the related {@link Statistics} or <code>null</code> if it cannot be found
*/
private static Statistics getStatistics(Class<?> target, String signature)
{
initIfNeeded();
Statistics statistics = MAPPING.get(signature);
if (statistics == null)
{
synchronized (JCRAPIAspect.class)
{
Class<?> interfaceClass = findInterface(target);
if (interfaceClass != null)
{
Map<String, Statistics> allStatistics = ALL_STATISTICS.get(interfaceClass.getSimpleName());
if (allStatistics != null)
{
int index1 = signature.indexOf('(');
int index = signature.substring(0, index1).lastIndexOf('.');
String name = signature.substring(index + 1);
statistics = allStatistics.get(name);
}
}
if (statistics == null)
{
statistics = UNKNOWN;
}
Map<String, Statistics> tempMapping = new HashMap<String, Statistics>(MAPPING);
tempMapping.put(signature, statistics);
MAPPING = Collections.unmodifiableMap(tempMapping);
}
}
if (statistics == UNKNOWN) // NOSONAR
{
return null;
}
return statistics;
}
/**
* Find the monitored interface from the target {@link Class}
* @param target the target {@link Class}
* @return the monitored interface, <code>null</code> otherwise
*/
private static Class<?> findInterface(Class<?> target)
{
if (target == null)
{
return null;
}
Class<?>[] interfaces = target.getInterfaces();
if (interfaces != null)
{
for (Class<?> c : TARGET_INTERFACES)
{
for (Class<?> i : interfaces)
{
if (c.getName().equals(i.getName()))
{
return c;
}
}
}
}
return findInterface(target.getSuperclass());
}
/**
* Initializes the aspect if needed
*/
private static void initIfNeeded()
{
if (!INITIALIZED)
{
synchronized (JCRAPIAspect.class)
{
if (!INITIALIZED)
{
ExoContainer container = ExoContainerContext.getTopContainer();
JCRAPIAspectConfig config = null;
if (container != null)
{
config = (JCRAPIAspectConfig)container.getComponentInstanceOfType(JCRAPIAspectConfig.class);
}
if (config == null)
{
TARGET_INTERFACES = new Class<?>[]{};
LOG.warn("No interface to monitor could be found");
}
else
{
TARGET_INTERFACES = config.getTargetInterfaces();
for (Class<?> c : TARGET_INTERFACES)
{
Statistics global = new Statistics(null, "global");
Map<String, Statistics> statistics = new TreeMap<String, Statistics>();
Method[] methods = c.getMethods();
for (Method m : methods)
{
String name = getStatisticsName(m);
statistics.put(name, new Statistics(global, name));
}
JCRStatisticsManager.registerStatistics(c.getSimpleName(), global, statistics);
ALL_STATISTICS.put(c.getSimpleName(), statistics);
}
}
INITIALIZED = true;
}
}
}
}
@Pointcut
abstract void JCRAPIPointcut();
@Before("JCRAPIPointcut()")
public void begin(JoinPoint thisJoinPoint)
{
Statistics statistics =
getStatistics(thisJoinPoint.getTarget().getClass(), thisJoinPoint.getSignature().toString());
if (statistics != null)
{
statistics.begin();
}
}
@After("JCRAPIPointcut()")
public void end(JoinPoint thisJoinPoint)
{
Statistics statistics =
getStatistics(thisJoinPoint.getTarget().getClass(), thisJoinPoint.getSignature().toString());
if (statistics != null)
{
statistics.end();
}
}
}