I m trying to externalize the configuration of aop.xml so I removed the aop.xml
from META-INF
and made it available in the server for manual config
I am not sure that there is an easy way out of your problem. As I said I haven't worked with AspectJ before but I believe this is a mis-behaviour of the weaver.
Problem description: During boot the agent tries to apply weaving other not only to the WebAppClassLoader
but to the whole classloader chain (once per classloader) i.e. to: sun.misc.Launcher$AppClassLoader
, sun.misc.Launcher$ExtClassLoader
, org.apache.catalina.loader.StandardClassLoader
(the tomcat's classloader). When you use the META-INF/aop.xml
approach it disables weaving for the above classloaders because "a configuration file is not available" (if you enable verbose
mode you can see those messages in console). When you use the file configuration approach, a configuration is available for all the classloaders in the chain. Since it does find a configuration file, the agent parses the definitions, it does not find the aspects' class and shows the error.
The weird thing is that, as described in the configuration documentation if you use the WeavingURLClassLoader
approach for load time weaving, "... it also allows the user to explicitly restrict by class loader which classes can be woven". So this is actually a feature (!) that the classloader approach can have but the agent approach doesn't. (Unfortunately I was not able to use this approach)
The good (and the bad) news: The good news is that you can easily create your own agent that will ignore the weaving for the aforementioned classloaders. The bad news is that restricting weaving per classloader is not enough because if you have other applications in the same server, Tomcat would still use the WebAppClassLoader
to load them so you would still get error messages for those applications. (Perhaps you could extend the classes below to filter packages/classes as well, in that case).
Below you can find two class for the modified agent. To use them you would need to do the following:
aspectjweaver.jar
to a folderorg/aspectj/weaver/loadtime
create a new folder filter
to match the package name and put there the two new classes after you compile them.Edit the META-INF/MANIFEST.MF
file and change the line
Premain-Class: org.aspectj.weaver.loadtime.Agent
to
Premain-Class: org.aspectj.weaver.loadtime.filter.FilterAgent
Re-jar and you have your new agent ready.
-Dorg.aspectj.weaver.loadtime.filter=sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader,org.apache.catalina.loader.StandardClassLoader
( I have set CATALINA_OPTS
to do that).The classes are a modified copy of the original agent's classes Agent
and ClassPreProcessorAgentAdapter
. The only code I have added is the part that parses the above system property if it exists and to ignore calls for the classloaders we are not interested in.
Use at your own risk :) I hope that helps
package org.aspectj.weaver.loadtime.filter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
public class FilterAgent {
private static Instrumentation s_instrumentation;
// Use our own version of ClassFileTransformer that would filter out selected classloaders
private static ClassFileTransformer s_transformer = new ClassPreprocessorFilteredAdapter();
/**
* JSR-163 preMain Agent entry method
*
* @param options
* @param instrumentation
*/
public static void premain(String options, Instrumentation instrumentation) {
/* Handle duplicate agents */
if (s_instrumentation != null) {
return;
}
s_instrumentation = instrumentation;
s_instrumentation.addTransformer(s_transformer);
}
public static Instrumentation getInstrumentation() {
if (s_instrumentation == null) {
throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for AspectJ");
}
return s_instrumentation;
}
}
//-----------------------------------------------------------------------------------
package org.aspectj.weaver.loadtime.filter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.weaver.loadtime.Aj;
import org.aspectj.weaver.loadtime.ClassPreProcessor;
public class ClassPreprocessorFilteredAdapter implements ClassFileTransformer {
/**
* Concrete preprocessor.
*/
private static ClassPreProcessor s_preProcessor;
private static Map<String, String> ignoredClassloaderNames = new HashMap<String, String>();
static {
try {
s_preProcessor = new Aj();
s_preProcessor.initialize();
String ignoredLoaders = System.getProperty("org.aspectj.weaver.loadtime.filter", "");
if (ignoredLoaders.length() > 0) {
String[] loaders = ignoredLoaders.split(",");
for (String s : loaders) {
s = s.trim();
ignoredClassloaderNames.put(s, s);
System.out.println("---> Will filtered out classloader: " + s);
}
}
} catch (Exception e) {
throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
}
}
/**
* Invokes the weaver to modify some set of input bytes.
*
* @param loader the defining class loader
* @param className the name of class being loaded
* @param classBeingRedefined is set when hotswap is being attempted
* @param protectionDomain the protection domain for the class being loaded
* @param bytes the incoming bytes (before weaving)
* @return the woven bytes
*/
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
if (classBeingRedefined != null) {
System.err.println("INFO: (Enh120375): AspectJ attempting reweave of '" + className + "'");
}
String loaderName = loader.getClass().getName();
if (shouldIgnoreClassLoader(loaderName)) {
return bytes;
}
return s_preProcessor.preProcess(className, bytes, loader, protectionDomain);
}
private boolean shouldIgnoreClassLoader(String loaderName) {
boolean result = false;
String ignoredLoader = ignoredClassloaderNames.get(loaderName);
if (ignoredLoader != null) {
result = true; // if the loader name exists in the map we will ignore weaving
}
return result;
}
}
What I ended up doing is changing the LOG Level for the error message from ERROR to DEBUG, as I don't see this as an ERROR ( at least in my case ). this case I can still see the error when I enable the DEBUG level. so I modified the source file below and rebuild my aspectjweaver-1.7.1.jar
try {
registerOptions(weaver, loader, definitions);
registerAspectExclude(weaver, loader, definitions);
registerAspectInclude(weaver, loader, definitions);
success = registerAspects(weaver, loader, definitions);
registerIncludeExclude(weaver, loader, definitions);
registerDump(weaver, loader, definitions);
} catch (Exception ex) {
//(CHANGE 1) trace.error("register definition failed", ex);
trace.debug( "register definition failed" + ex.getMessage());
success = false;
// (CHANGE 2) warn("register definition failed", (ex instanceof AbortException) ? null : ex);
debug("register definition failed" + ((ex instanceof AbortException) ? null : ex));
}
If you need the feature to exclude classloaders from weaving with the agent approach, there is a developer build available providing a new command line switch -Daj.weaving.loadersToSkip
to do that. The topic is being discussed on a thread of the AspectJ users mailing list. The feature will probably make it into AspectJ 1.7.4, but is not available in 1.7.3 yet.
Update:
The feature did make it into AspectJ 1.7.4 even though it is not explicitly mentioned in the release notes, but listed under resolved issues for that release.