package org.stagemonitor.core.instrument;
import java.util.ArrayList;
import java.util.Collection;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.Stagemonitor;
/**
* An {@link ElementMatcher} with the following logic:
* <ul>
* <li>Exclude all types which contain <code>stagemonitor.instrument.excludeContaining</code></li>
* <li>Include all types <code>stagemonitor.instrument.include</code></li>
* <li>If there are no more specific excludes in <code>stagemonitor.instrument.exclude</code></li>
* </ul>
*/
public class StagemonitorClassNameMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {
private static final Logger logger = LoggerFactory.getLogger(StagemonitorClassNameMatcher.class);
private static Collection<String> includes;
private static Collection<String> excludes;
private static Collection<String> excludeContaining;
public static final StagemonitorClassNameMatcher INSTANCE = new StagemonitorClassNameMatcher();
public static ElementMatcher.Junction<TypeDescription> isInsideMonitoredProject() {
return INSTANCE;
}
private StagemonitorClassNameMatcher() {
}
static {
initIncludesAndExcludes();
}
private static void initIncludesAndExcludes() {
CorePlugin corePlugin = Stagemonitor.getPlugin(CorePlugin.class);
excludeContaining = new ArrayList<String>(corePlugin.getExcludeContaining().size());
for (String exclude : corePlugin.getExcludeContaining()) {
excludeContaining.add(exclude);
}
excludes = new ArrayList<String>(corePlugin.getExcludePackages().size());
excludes.add("org.stagemonitor");
for (String exclude : corePlugin.getExcludePackages()) {
excludes.add(exclude);
}
includes = new ArrayList<String>(corePlugin.getIncludePackages().size());
for (String include : corePlugin.getIncludePackages()) {
includes.add(include);
}
if (includes.isEmpty()) {
logger.warn("No includes for instrumentation configured. Please set the stagemonitor.instrument.include property.");
}
}
/**
* Checks if a specific class should be instrumented with this instrumenter
* <p/>
* The default implementation considers the following properties:
* <ul>
* <li><code>stagemonitor.instrument.excludeContaining</code></li>
* <li><code>stagemonitor.instrument.include</code></li>
* <li><code>stagemonitor.instrument.exclude</code></li>
* </ul>
*
* @param className The name of the class. For example java/lang/String
* @return <code>true</code>, if the class should be instrumented, <code>false</code> otherwise
*/
public static boolean isIncluded(String className) {
for (String exclude : excludeContaining) {
if (className.contains(exclude)) {
return false;
}
}
for (String include : includes) {
if (className.startsWith(include)) {
return !hasMoreSpecificExclude(className, include);
}
}
return false;
}
private static boolean hasMoreSpecificExclude(String className, String include) {
for (String exclude : excludes) {
if (exclude.length() > include.length() && exclude.startsWith(include) && className.startsWith(exclude)) {
return true;
}
}
return false;
}
@Override
public boolean matches(TypeDescription target) {
return isIncluded(target.getName());
}
}