org.aspectj.weaver.tools.cache.WeavedClassCache Maven / Gradle / Ivy
Show all versions of aspectjtools Show documentation
/*******************************************************************************
* Copyright (c) 2012 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* John Kew (vmware) initial implementation
* Lyor Goldstein (vmware) add support for weaved class being re-defined
*******************************************************************************/
package org.aspectj.weaver.tools.cache;
import java.util.LinkedList;
import java.util.List;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.weaver.tools.GeneratedClassHandler;
/**
* Manages a cache of weaved and generated classes similar to Eclipse Equinox,
* except designed to operate across multiple restarts of the JVM and with one
* cache per classloader; allowing URLClassLoaders with the same set of URI
* paths to share the same cache (with the default configuration).
*
* To enable the default configuration two system properties must be set:
*
*
* "-Daj.weaving.cache.enabled=true"
* "-Daj.weaving.cache.dir=/some/directory"
*
*
* The class cache is often something that application developers or
* containers would like to manage, so there are a few interfaces for overriding the
* default behavior and performing other management functions.
*
*
* {@link CacheBacking}
* Provides an interface for implementing a custom backing store
* for the cache; The default implementation in {@link DefaultFileCacheBacking}
* provides a naive file-backed cache. An alternate implementation may ignore
* caching until signaled explicitly by the application, or only cache files
* for a specific duration. This class delegates the locking and synchronization
* requirements to the CacheBacking implementation.
*
*
* {@link CacheKeyResolver}
* Provides methods for creating keys from classes to be cached and for
* creating the "scope" of the cache itself for a given classloader and aspect
* list. The default implementation is provided by {@link DefaultCacheKeyResolver}
* but an alternate implementation may want to associate a cache with a particular
* application running underneath a container.
*
*
* This naive cache does not normally invalidate *any* classes; the interfaces above
* must be used to implement more intelligent behavior. Cache invalidation
* problems may occur in at least three scenarios:
*
*
* - New aspects are added dynamically somewhere in the classloader hierarchy; affecting
* other classes elsewhere.
* - Use of declare parent in aspects to change the type hierarchy; if the cache
* has not invalidated the right classes in the type hierarchy aspectj may not
* be reconstruct the class incorrectly.
* - Similarly to (2), the addition of fields or methods on classes which have
* already been weaved and cached could have inter-type conflicts.
*
*/
public class WeavedClassCache {
public static final String WEAVED_CLASS_CACHE_ENABLED = "aj.weaving.cache.enabled";
public static final String CACHE_IMPL = SimpleCacheFactory.CACHE_IMPL;
private static CacheFactory DEFAULT_FACTORY = new DefaultCacheFactory();
public static final byte[] ZERO_BYTES = new byte[0];
private final IMessageHandler messageHandler;
private final GeneratedCachedClassHandler cachingClassHandler;
private final CacheBacking backing;
private final CacheStatistics stats;
private final CacheKeyResolver resolver;
private final String name;
private static final List cacheRegistry = new LinkedList<>();
protected WeavedClassCache(GeneratedClassHandler existingClassHandler,
IMessageHandler messageHandler,
String name,
CacheBacking backing,
CacheKeyResolver resolver) {
this.resolver = resolver;
this.name = name;
this.backing = backing;
this.messageHandler = messageHandler;
// wrap the existing class handler with a caching version
cachingClassHandler = new GeneratedCachedClassHandler(this, existingClassHandler);
this.stats = new CacheStatistics();
synchronized (cacheRegistry) {
cacheRegistry.add(this);
}
}
/**
* Creates a new cache using the resolver and backing returned by the DefaultCacheFactory.
*
* @param loader classloader for this cache
* @param aspects list of aspects used by the WeavingAdapter
* @param existingClassHandler the existing GeneratedClassHandler used by the weaver
* @param messageHandler the existing messageHandler used by the weaver
* @return
*/
public static WeavedClassCache createCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler messageHandler) {
CacheKeyResolver resolver = DEFAULT_FACTORY.createResolver();
String name = resolver.createClassLoaderScope(loader, aspects);
if (name == null) {
return null;
}
CacheBacking backing = DEFAULT_FACTORY.createBacking(name);
if (backing != null) {
return new WeavedClassCache(existingClassHandler, messageHandler, name, backing, resolver);
}
return null;
}
public String getName() {
return name;
}
/**
* The Cache and be extended in two ways, through a specialized CacheKeyResolver and
* a specialized CacheBacking. The default factory used to create these classes can
* be set with this method. Since each weaver will create a cache, this method must be
* called before the weaver is first initialized.
*
* @param factory
*/
public static void setDefaultCacheFactory(CacheFactory factory) {
DEFAULT_FACTORY = factory;
}
/**
* Created a key for a generated class
*
* @param className ClassName, e.g. "com.foo.Bar"
* @return the cache key, or null if no caching should be performed
*/
public CachedClassReference createGeneratedCacheKey(String className) {
return resolver.generatedKey(className);
}
/**
* Create a key for a normal weaved class
*
* @param className ClassName, e.g. "com.foo.Bar"
* @param originalBytes Original byte array of the class
* @return a cache key, or null if no caching should be performed
*/
public CachedClassReference createCacheKey(String className, byte[] originalBytes) {
return resolver.weavedKey(className, originalBytes);
}
/**
* Returns a generated class handler which wraps the handler this cache was initialized
* with; this handler should be used to make sure that generated classes are added
* to the cache
*/
public GeneratedClassHandler getCachingClassHandler() {
return cachingClassHandler;
}
/**
* Has caching been enabled through the System property,
* WEAVED_CLASS_CACHE_ENABLED
*
* @return true if caching is enabled
*/
public static boolean isEnabled() {
String enabled = System.getProperty(WEAVED_CLASS_CACHE_ENABLED);
String impl = System.getProperty(CACHE_IMPL);
return (enabled != null && (impl == null || !SimpleCache.IMPL_NAME.equalsIgnoreCase(impl) ) );
}
/**
* Put a class in the cache
*
* @param ref reference to the entry, as created through createCacheKey
* @param classBytes pre-weaving class bytes
* @param weavedBytes bytes to cache
*/
public void put(CachedClassReference ref, byte[] classBytes, byte[] weavedBytes) {
CachedClassEntry.EntryType type = CachedClassEntry.EntryType.WEAVED;
if (ref.getKey().matches(resolver.getGeneratedRegex())) {
type = CachedClassEntry.EntryType.GENERATED;
}
backing.put(new CachedClassEntry(ref, weavedBytes, type), classBytes);
stats.put();
}
/**
* Get a cache value
*
* @param ref reference to the cache entry, created through createCacheKey
* @param classBytes Pre-weaving class bytes - required to ensure that
* cached aspects refer to an unchanged original class
* @return the CacheEntry, or null if no entry exists in the cache
*/
public CachedClassEntry get(CachedClassReference ref, byte[] classBytes) {
CachedClassEntry entry = backing.get(ref, classBytes);
if (entry == null) {
stats.miss();
} else {
stats.hit();
if (entry.isGenerated()) stats.generated();
if (entry.isWeaved()) stats.weaved();
if (entry.isIgnored()) stats.ignored();
}
return entry;
}
/**
* Put a cache entry to indicate that the class should not be
* weaved; the original bytes of the class should be used.
*
* @param ref The cache reference
* @param classBytes The un-weaved class bytes
*/
public void ignore(CachedClassReference ref, byte[] classBytes) {
stats.putIgnored();
backing.put(new CachedClassEntry(ref, ZERO_BYTES, CachedClassEntry.EntryType.IGNORED), classBytes);
}
/**
* Invalidate a cache entry
*
* @param ref
*/
public void remove(CachedClassReference ref) {
backing.remove(ref);
}
/**
* Clear the entire cache
*/
public void clear() {
backing.clear();
}
/**
* Get the statistics associated with this cache, or
* null if statistics have not been enabled.
*
* @return
*/
public CacheStatistics getStats() {
return stats;
}
/**
* Return a list of all WeavedClassCaches which have been initialized
*
* @return
*/
public static List getCaches() {
synchronized (cacheRegistry) {
return new LinkedList<>(cacheRegistry);
}
}
protected void error(String message, Throwable th) {
messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null));
}
protected void error(String message) {
MessageUtil.error(messageHandler, message);
}
protected void info(String message) {
MessageUtil.info(message);
}
}