Java tutorial
/******************************************************************************* * Copyright (c) 2006, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * David Knibb initial implementation * Matthew Webster Eclipse 3.2 changes * Heiko Seeberger AJDT 1.5.1 changes * Martin Lippert minor changes and bugfixes * Martin Lippert reworked * Martin Lippert caching of generated classes * Martin Lippert added locking for weaving *******************************************************************************/ package org.eclipse.equinox.weaving.aspectj.loadtime; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import org.aspectj.weaver.IUnwovenClassFile; import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference; import org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor; import org.aspectj.weaver.tools.GeneratedClassHandler; import org.aspectj.weaver.tools.Trace; import org.aspectj.weaver.tools.TraceFactory; import org.eclipse.equinox.weaving.aspectj.AspectJWeavingStarter; /** * The weaving adaptor for AspectJs load-time weaving API that deals with the * OSGi specifics for load-time weaving */ public class OSGiWeavingAdaptor extends ClassLoaderWeavingAdaptor { /** * internal class to collect generated classes (produced by the weaving) to * define then after the weaving itself */ class GeneratedClass { private final byte[] bytes; private final String name; public GeneratedClass(final String name, final byte[] bytes) { this.name = name; this.bytes = bytes; } public byte[] getBytes() { return bytes; } public String getName() { return name; } } /** * generated class handler to collect generated classes (produced by the * weaving) to define then after the weaving itself */ class OSGiGeneratedClassHandler implements GeneratedClassHandler { private final ConcurrentLinkedQueue<GeneratedClass> classesToBeDefined; private final BcelWeakClassLoaderReference loaderRef; public OSGiGeneratedClassHandler(final ClassLoader loader) { loaderRef = new BcelWeakClassLoaderReference(loader); classesToBeDefined = new ConcurrentLinkedQueue<GeneratedClass>(); } /** * Callback when we need to define a generated class in the JVM (version * for older AspectJ versions) */ public void acceptClass(final String name, final byte[] bytes) { try { if (shouldDump(name.replace('/', '.'), false)) { dump(name, bytes, false); } } catch (final Throwable throwable) { throwable.printStackTrace(); } classesToBeDefined.offer(new GeneratedClass(name, bytes)); } /** * Callback when we need to define a generated class in the JVM (version * for newer AspectJ versions, but we can ignore the originalBytes here) */ public void acceptClass(final String name, final byte[] originalBytes, final byte[] weavedBytes) { acceptClass(name, weavedBytes); } public void defineGeneratedClasses() { while (!classesToBeDefined.isEmpty()) { final GeneratedClass generatedClass = classesToBeDefined.poll(); if (generatedClass != null) { defineClass(loaderRef.getClassLoader(), generatedClass.getName(), generatedClass.getBytes()); } else { break; } } } } private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassLoaderWeavingAdaptor.class); private final ClassLoader classLoader; private Method defineClassMethod; private boolean initialized; private final Object initializeLock = new Object(); private final String namespace; private final OSGiWeavingContext weavingContext; /** * The OSGi weaving adaptor provides a bridge to the AspectJ weaving adaptor * implementation for general classloaders. This weaving adaptor exists per * bundle that should be woven. * * @param loader The classloader of the bundle to be woven * @param context The bridge to the weaving context * @param namespace The namespace of this adaptor, some kind of unique ID * for this weaver */ public OSGiWeavingAdaptor(final ClassLoader loader, final OSGiWeavingContext context, final String namespace) { super(); this.classLoader = loader; this.weavingContext = context; this.namespace = namespace; } private void defineClass(final ClassLoader loader, final String name, final byte[] bytes) { if (trace.isTraceEnabled()) { trace.enter("defineClass", this, new Object[] { loader, name, bytes }); } Object clazz = null; debug("generating class '" + name + "'"); try { if (defineClassMethod == null) { defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, bytes.getClass(), int.class, int.class }); } defineClassMethod.setAccessible(true); clazz = defineClassMethod.invoke(loader, new Object[] { name, bytes, new Integer(0), new Integer(bytes.length) }); } catch (final InvocationTargetException e) { if (e.getTargetException() instanceof LinkageError) { warn("define generated class failed", e.getTargetException()); // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved) // TODO maw I don't think this is OK and } else { warn("define generated class failed", e.getTargetException()); } } catch (final Exception e) { warn("define generated class failed", e); } if (trace.isTraceEnabled()) { trace.exit("defineClass", clazz); } } /** * In some situations the weaving creates new classes on the fly that are * not part of the original bundle. This is the case when the weaver needs * to create closure-like constructs for the woven code. * * This method returns a map of the generated classes (name -> bytecode) and * flushes the internal cache afterwards to avoid memory damage over time. * * @param className The name of the class for which additional classes might * got generated * @return the map of generated class names and bytecodes for those * generated classes */ public Map<String, byte[]> getGeneratedClassesFor(final String className) { final Map<?, ?> generated = this.generatedClasses; final Map<String, byte[]> result = new HashMap<String, byte[]>(); final Iterator<?> generatedClassNames = generated.keySet().iterator(); while (generatedClassNames.hasNext()) { final String name = (String) generatedClassNames.next(); final IUnwovenClassFile unwovenClass = (IUnwovenClassFile) generated.get(name); if (!className.equals(name)) { result.put(name, unwovenClass.getBytes()); } } flushGeneratedClasses(); return result; } /** * @see org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor#getNamespace() */ @Override public String getNamespace() { return namespace; } /** * initialize the weaving adaptor */ public void initialize() { synchronized (initializeLock) { if (!initialized) { super.initialize(classLoader, weavingContext); this.generatedClassHandler = new OSGiGeneratedClassHandler(classLoader); initialized = true; if (AspectJWeavingStarter.verbose) { if (isEnabled()) { System.err.println("[org.eclipse.equinox.weaving.aspectj] info weaving bundle '" + weavingContext.getClassLoaderName() + "'"); } else { System.err.println("[org.eclipse.equinox.weaving.aspectj] info not weaving bundle '" + weavingContext.getClassLoaderName() + "'"); } } } } } /** * @see org.aspectj.weaver.tools.WeavingAdaptor#weaveClass(java.lang.String, * byte[], boolean) */ @Override public byte[] weaveClass(final String name, byte[] bytes, final boolean mustWeave) throws IOException { /* Avoid recursion during adaptor initialization */ synchronized (initializeLock) { if (!initialized) { super.initialize(classLoader, weavingContext); this.generatedClassHandler = new OSGiGeneratedClassHandler(classLoader); initialized = true; } } synchronized (this) { bytes = super.weaveClass(name, bytes, mustWeave); } ((OSGiGeneratedClassHandler) this.generatedClassHandler).defineGeneratedClasses(); return bytes; } }