package gw.lang.parser; import gw.lang.reflect.IType; import gw.lang.reflect.gs.ICompilableType; import gw.lang.reflect.gs.IGosuProgram; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Copyright 2010 Guidewire Software, Inc. */ public final class InstrumentationManager { public static ThreadLocal<Boolean> IS_TESTER_DEBUGGER = new ThreadLocal<Boolean>(); private static final Map<IRuntimeObserver,Integer> _observers = Collections.synchronizedMap( new LinkedHashMap<IRuntimeObserver,Integer>() ); private static Set<String> _blacklist = generateBlacklist(); private static Set<String> _packageBlacklist = generatePackageBlacklist(); private static final boolean _forceInstrumentation = Boolean.getBoolean("gwdebug"); private static final List<IBytecodeTransformer> _transformers = Collections.synchronizedList( new ArrayList() ); private InstrumentationManager() { // Static-only access } public static void addRuntimeObserver( IRuntimeObserver observer ) { synchronized( _observers ) { Integer count = _observers.get( observer ); _observers.put( observer, count == null ? 1 : count + 1 ); } } public static void removeRuntimeObserver( IRuntimeObserver observer ) { synchronized( _observers ) { Integer count = _observers.get( observer ); if( count != null ) { if( count == 0 ) { throw new IllegalStateException(); } if( count == 1 ) { _observers.remove( observer ); } else { _observers.put( observer, count-1 ); } } } } // Called via instrumentation @SuppressWarnings({"UnusedDeclaration"}) public static void onBeforeExecute() { for( IRuntimeObserver observer : getObservers()) { RuntimeInfoAtStatement ctx = getCtx(); if( observer.observes( ctx.getEnclosingType() ) ) { observer.onBeforeExecute( ctx ); } } } private static Set<IRuntimeObserver> getObservers() { synchronized (_observers) { return new HashSet<IRuntimeObserver>(_observers.keySet()); } } public static boolean shouldInstrument( ICompilableType gosuClass ) { if( gosuClass instanceof IGosuProgram ) { // Programs are not debuggable in studio return false; } if( _blacklist.contains( gosuClass.getName() ) ) { // some classes should never be instrumented, e.g. gw.internal.webservice.studio.StudioService return false; } if( _packageBlacklist.contains( gosuClass.getNamespace() )) { // some packages are not instrumented, e.g. gw.xml.ws.annotation.* return false; } if( _forceInstrumentation ) { return true; } for( IRuntimeObserver observer : getObservers()) { if( observer.observes( gosuClass ) ) { return true; } } return false; } public static RuntimeInfoAtStatement getCtx() { return RuntimeInfoAtStatement.getCtx(); } public static boolean hasObservers() { return !_observers.isEmpty(); } public static boolean isTesterDebugger() { Boolean b = IS_TESTER_DEBUGGER.get(); return b == null ? false : b; } private static Set<String> generateBlacklist() { Set<String> blacklist = Collections.synchronizedSet(new HashSet<String>()); blacklist.add( "gw.internal.webservice.studio.StudioService" ); blacklist.add( "gw.lang.RpcWebService" ); blacklist.add( "gw.lang.RpcWebServiceMethod" ); blacklist.add( "gw.servlet.Servlet" ); return blacklist; } private static Set<String> generatePackageBlacklist() { Set<String> packageBlacklist = Collections.synchronizedSet(new HashSet<String>()); packageBlacklist.add( "gw.xml.ws"); packageBlacklist.add( "gw.xml.ws.annotation"); packageBlacklist.add( "gw.internal.xml.ws" ); // PL-15357 - Workaround JVM crash caused by certain CC sample data class methods when debugging smoketests. // Can be removed if the cause of the crash (possibly malformed bytecode) is fixed. packageBlacklist.add( "gw.sampledata"); return packageBlacklist; } public static void blacklistClass(String gosuClassName) { _blacklist.add(gosuClassName); } public static class RetryCompileException extends RuntimeException { public RetryCompileException(String gosuClassName, ClassFormatError ve) { super("Code size for " + gosuClassName + " is too large to support debug instrumentation. Debugging this class will be disabled.", ve); } } public static void addBytecodeTransformer( IBytecodeTransformer transformer ) { synchronized( _transformers ) { _transformers.add( transformer ); } } public static byte[] transform( IType type, byte[] bytecode ) { if( !_transformers.isEmpty() ) { synchronized( _transformers ) { for( IBytecodeTransformer transformer : _transformers ) { bytecode = transformer.transformBytes( type, bytecode ); } } } return bytecode; } public interface IBytecodeTransformer { byte[] transformBytes( IType type, byte[] bytes ); } }