diff options
author | Andy Clement <andrew.clement@gmail.com> | 2012-10-01 15:09:15 -0700 |
---|---|---|
committer | Andy Clement <andrew.clement@gmail.com> | 2012-10-01 15:09:15 -0700 |
commit | 8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917 (patch) | |
tree | eb9e96e988a63e1aa48896b279adae9bc41f9084 /weaver | |
parent | 9a3cc2bc5c824d252140fb3d1e2e27f2163e6d53 (diff) | |
download | aspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.tar.gz aspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.zip |
386341
Diffstat (limited to 'weaver')
9 files changed, 949 insertions, 1 deletions
diff --git a/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java b/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java index 8b31ce586..48ef6ae8a 100644 --- a/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java +++ b/weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java @@ -46,6 +46,8 @@ import org.aspectj.weaver.bcel.BcelWorld; import org.aspectj.weaver.bcel.UnwovenClassFile; import org.aspectj.weaver.tools.cache.CachedClassEntry; import org.aspectj.weaver.tools.cache.CachedClassReference; +import org.aspectj.weaver.tools.cache.SimpleCache; +import org.aspectj.weaver.tools.cache.SimpleCacheFactory; import org.aspectj.weaver.tools.cache.WeavedClassCache; // OPTIMIZE add guards for all the debug/info/etc @@ -882,6 +884,13 @@ public class WeavingAdaptor implements IMessageContext { // Classes generated by weaver e.g. around closure advice String className = result.getClassName(); byte[] resultBytes = result.getBytes(); + + if (SimpleCacheFactory.isEnabled()) { + SimpleCache lacache=SimpleCacheFactory.createSimpleCache(); + lacache.put(result.getClassName(), wovenClass.getBytes(), result.getBytes()); + lacache.addGeneratedClassesNames(wovenClass.getClassName(), wovenClass.getBytes(), result.getClassName()); + } + generatedClasses.put(className, result); generatedClasses.put(wovenClass.getClassName(), result); generatedClassHandler.acceptClass(className, null, resultBytes); diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java new file mode 100644 index 000000000..649e21d05 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * 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.zip.CRC32; + +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + +/** + * Basic "common" {@link CacheBacking} implementation + */ +public abstract class AbstractCacheBacking implements CacheBacking { + protected final Trace logger=TraceFactory.getTraceFactory().getTrace(getClass()); + + protected AbstractCacheBacking () { + super(); + } + + /** + * Calculates CRC32 on the provided bytes + * @param bytes The bytes array - ignored if <code>null</code>/empty + * @return Calculated CRC + * @see {@link CRC32} + */ + public static final long crc (byte[] bytes) { + if ((bytes == null) || (bytes.length <= 0)) { + return 0L; + } + + CRC32 crc32=new CRC32(); + crc32.update(bytes); + return crc32.getValue(); + } +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java new file mode 100644 index 000000000..5447c158b --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * 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.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Useful "common" functionality for caching to files + */ +public abstract class AbstractFileCacheBacking extends AbstractCacheBacking { + /** + * Default property used to specify a default weaving cache dir location + */ + public static final String WEAVED_CLASS_CACHE_DIR = "aj.weaving.cache.dir"; + private final File cacheDirectory; + + protected AbstractFileCacheBacking (File cacheDirectory) { + if ((this.cacheDirectory=cacheDirectory) == null) { + throw new IllegalStateException("No cache directory specified"); + } + } + + public File getCacheDirectory () { + return cacheDirectory; + } + + protected void writeClassBytes (String key, byte[] bytes) throws Exception { + File dir=getCacheDirectory(), file=new File(dir, key); + FileOutputStream out=new FileOutputStream(file); + try { + out.write(bytes); + } finally { + close(out, file); + } + } + + protected void delete(File file) { + if (file.exists() && (!file.delete())) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Error deleting file " + file.getAbsolutePath()); + } + } + } + + protected void close(OutputStream out, File file) { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close write file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } + + protected void close(InputStream in, File file) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close read file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java new file mode 100644 index 000000000..6653c9bdf --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * 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.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StreamCorruptedException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.LangUtil; + +/** + * Uses an <code>index</code> file to keep track of the cached entries + */ +public abstract class AbstractIndexedFileCacheBacking extends AbstractFileCacheBacking { + /** + * Default name of cache index file - assumed to contain {@link IndexEntry}-s + */ + public static final String INDEX_FILE = "cache.idx"; + protected static final IndexEntry[] EMPTY_INDEX=new IndexEntry[0]; + protected static final String[] EMPTY_KEYS=new String[0]; + + private final File indexFile; + + protected AbstractIndexedFileCacheBacking(File cacheDir) { + super(cacheDir); + + indexFile = new File(cacheDir, INDEX_FILE); + } + + public File getIndexFile () { + return indexFile; + } + + public String[] getKeys(String regex) { + Map<String, IndexEntry> index=getIndex(); + if ((index == null) || index.isEmpty()) { + return EMPTY_KEYS; + } + Collection<String> matches=new LinkedList<String>(); + synchronized(index) { + for (String key : index.keySet()) { + if (key.matches(regex)) { + matches.add(key); + } + } + } + + if (matches.isEmpty()) { + return EMPTY_KEYS; + } else { + return matches.toArray(new String[matches.size()]); + } + } + + protected Map<String, IndexEntry> readIndex () { + return readIndex(getCacheDirectory(), getIndexFile()); + } + + protected void writeIndex () { + writeIndex(getIndexFile()); + } + + protected void writeIndex (File file) { + try { + writeIndex(file, getIndex()); + } catch(Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.warn("writeIndex(" + file + ") " + e.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + } + + protected abstract Map<String, IndexEntry> getIndex (); + + protected Map<String, IndexEntry> readIndex (File cacheDir, File cacheFile) { + Map<String, IndexEntry> indexMap=new TreeMap<String, IndexEntry>(); + IndexEntry[] idxValues=readIndex(cacheFile); + if (LangUtil.isEmpty(idxValues)) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("readIndex(" + cacheFile + ") no index entries"); + } + return indexMap; + } + + for (IndexEntry ie : idxValues) { + IndexEntry resEntry=resolveIndexMapEntry(cacheDir, ie); + if (resEntry != null) { + indexMap.put(resEntry.key, resEntry); + } else if ((logger != null) && logger.isTraceEnabled()) { + logger.debug("readIndex(" + cacheFile + ") skip " + ie.key); + } + } + + return indexMap; + } + + protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) { + return ie; + } + + public IndexEntry[] readIndex(File indexFile) { + if (!indexFile.canRead()) { + return EMPTY_INDEX; + } + + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(new FileInputStream(indexFile)); + return (IndexEntry[]) ois.readObject(); + } catch (Exception e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to read index from " + indexFile.getAbsolutePath() + + " : " + e.getMessage(), e); + } + delete(indexFile); + } finally { + close(ois, indexFile); + } + + return EMPTY_INDEX; + } + + protected void writeIndex (File indexFile, Map<String,? extends IndexEntry> index) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(index) ? Collections.<IndexEntry>emptyList() : index.values()); + } + + protected void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.<IndexEntry>emptyList() : Arrays.asList(entries)); + } + + protected void writeIndex (File indexFile, Collection<? extends IndexEntry> entries) throws IOException { + File indexDir=indexFile.getParentFile(); + if ((!indexDir.exists()) && (!indexDir.mkdirs())) { + throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); + } + + int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); + IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); + // if no entries, simply delete the index file + if (LangUtil.isEmpty(entryValues)) { + if (indexFile.exists() && (!indexFile.delete())) { + throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); + } + + return; + } + + ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); + try { + oos.writeObject(entryValues); + } finally { + close(oos, indexFile); + } + } + + /** + * The default index entry in the index file + */ + public static class IndexEntry implements Serializable, Cloneable { + private static final long serialVersionUID = 756391290557029363L; + + public String key; + public boolean generated; + public boolean ignored; + public long crcClass; + public long crcWeaved; + + public IndexEntry () { + super(); + } + + @Override + public IndexEntry clone () { + try { + return getClass().cast(super.clone()); + } catch(CloneNotSupportedException e) { + throw new RuntimeException("Failed to clone: " + toString() + ": " + e.getMessage(), e); + } + } + + @Override + public int hashCode() { + return (int) (key.hashCode() + + (generated ? 1 : 0) + + (ignored ? 1 : 0) + + crcClass + + crcWeaved); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + + IndexEntry other=(IndexEntry) obj; + if (this.key.equals(other.key) + && (this.ignored == other.ignored) + && (this.generated == other.generated) + && (this.crcClass == other.crcClass) + && (this.crcWeaved == other.crcWeaved)) { + return true; + } else { + return false; + } + } + + @Override + public String toString() { + return key + + "[" + (generated ? "generated" : "ignored") + "]" + + ";crcClass=0x" + Long.toHexString(crcClass) + + ";crcWeaved=0x" + Long.toHexString(crcWeaved) + ; + } + } + +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java new file mode 100644 index 000000000..45d718a14 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java @@ -0,0 +1,377 @@ +package org.aspectj.weaver.tools.cache; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.CRC32; + +import org.aspectj.weaver.Dump; +import org.aspectj.weaver.tools.Trace; +import org.aspectj.weaver.tools.TraceFactory; + + +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +public class SimpleCache { + + private static final String SAME_BYTES_STRING = "IDEM"; + private static final byte[] SAME_BYTES = SAME_BYTES_STRING.getBytes(); + + private Map<String, byte[]> cacheMap; + private boolean enabled = false; + + // cache for generated classes + private Map<String, byte[]> generatedCache; + private static final String GENERATED_CACHE_SUBFOLDER = "panenka.cache"; + private static final String GENERATED_CACHE_SEPARATOR = ";"; + + public static final String IMPL_NAME = "shared"; + + protected SimpleCache(String folder, boolean enabled) { + this.enabled = enabled; + + cacheMap = Collections.synchronizedMap(StoreableCachingMap.init(folder)); + + if (enabled) { + String generatedCachePath = folder + File.separator + GENERATED_CACHE_SUBFOLDER; + File f = new File (generatedCachePath); + if (!f.exists()){ + f.mkdir(); + } + generatedCache = Collections.synchronizedMap(StoreableCachingMap.init(generatedCachePath,0)); + } + } + + public byte[] getAndInitialize(String classname, byte[] bytes, + ClassLoader loader, ProtectionDomain protectionDomain) { + if (!enabled) { + return null; + } + byte[] res = get(classname, bytes); + + if (Arrays.equals(SAME_BYTES, res)) { + return bytes; + } else { + if (res != null) { + initializeClass(classname, res, loader, protectionDomain); + } + return res; + } + + } + + private byte[] get(String classname, byte bytes[]) { + String key = generateKey(classname, bytes); + + byte[] res = cacheMap.get(key); + return res; + } + + public void put(String classname, byte[] origbytes, byte[] wovenbytes) { + if (!enabled) { + return; + } + + String key = generateKey(classname, origbytes); + + if (Arrays.equals(origbytes, wovenbytes)) { + cacheMap.put(key, SAME_BYTES); + return; + } + cacheMap.put(key, wovenbytes); + } + + private String generateKey(String classname, byte[] bytes) { + CRC32 checksum = new CRC32(); + checksum.update(bytes); + long crc = checksum.getValue(); + classname = classname.replace("/", "."); + return new String(classname + "-" + crc); + + } + + private static class StoreableCachingMap extends HashMap { + private String folder; + private static final String CACHENAMEIDX = "cache.idx"; + + private long lastStored = System.currentTimeMillis(); + private static int DEF_STORING_TIMER = 60000; //ms + private int storingTimer; + + private transient Trace trace; + private void initTrace(){ + trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); + } + +// private StoreableCachingMap(String folder) { +// this.folder = folder; +// initTrace(); +// } + + private StoreableCachingMap(String folder, int storingTimer){ + this.folder = folder; + initTrace(); + this.storingTimer = storingTimer; + } + + public static StoreableCachingMap init(String folder) { + return init(folder,DEF_STORING_TIMER); + + } + + public static StoreableCachingMap init(String folder, int storingTimer) { + File file = new File(folder + File.separator + CACHENAMEIDX); + if (file.exists()) { + try { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream(file)); + // Deserialize the object + StoreableCachingMap sm = (StoreableCachingMap) in.readObject(); + sm.initTrace(); + in.close(); + return sm; + } catch (Exception e) { + Trace trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class); + trace.error("Error reading Storable Cache", e); + } + } + + return new StoreableCachingMap(folder,storingTimer); + + } + + @Override + public Object get(Object obj) { + try { + if (super.containsKey(obj)) { + String path = (String) super.get(obj); + if (path.equals(SAME_BYTES_STRING)) { + return SAME_BYTES; + } + return readFromPath(path); + } else { + return null; + } + } catch (IOException e) { + trace.error("Error reading key:"+obj.toString(),e); + Dump.dumpWithException(e); + } + return null; + } + + @Override + public Object put(Object key, Object value) { + try { + String path = null; + byte[] valueBytes = (byte[]) value; + + if (Arrays.equals(valueBytes, SAME_BYTES)) { + path = SAME_BYTES_STRING; + } else { + path = writeToPath((String) key, valueBytes); + } + Object result = super.put(key, path); + storeMap(); + return result; + } catch (IOException e) { + trace.error("Error inserting in cache: key:"+key.toString() + "; value:"+value.toString(), e); + Dump.dumpWithException(e); + } + return null; + } + + + + public void storeMap() { + long now = System.currentTimeMillis(); + if ((now - lastStored ) < storingTimer){ + return; + } + File file = new File(folder + File.separator + CACHENAMEIDX);; + try { + ObjectOutputStream out = new ObjectOutputStream( + new FileOutputStream(file)); + // Deserialize the object + out.writeObject(this); + out.close(); + lastStored = now; + } catch (Exception e) { + trace.error("Error storing cache; cache file:"+file.getAbsolutePath(), e); + Dump.dumpWithException(e); + } + } + + private byte[] readFromPath(String fullPath) throws IOException { + FileInputStream is = null ; + try{ + is = new FileInputStream(fullPath); + } + catch (FileNotFoundException e){ + //may be caused by a generated class that has been stored in generated cache but not saved at cache folder + System.out.println("FileNotFoundExceptions: The aspectj cache is corrupt. Please clean it and reboot the server. Cache path:"+this.folder ); + e.printStackTrace(); + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[16384]; + + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + buffer.flush(); + is.close(); + return buffer.toByteArray(); + + } + + private String writeToPath(String key, byte[] bytes) throws IOException { + String fullPath = folder + File.separator + key; + FileOutputStream fos = new FileOutputStream(fullPath); + fos.write(bytes); + fos.flush(); + fos.close(); + return fullPath; + } + + } + + private void initializeClass(String className, byte[] bytes, + ClassLoader loader, ProtectionDomain protectionDomain) { + String[] generatedClassesNames = getGeneratedClassesNames(className,bytes); + + if (generatedClassesNames == null) { + return; + } + for (String generatedClassName : generatedClassesNames) { + + byte[] generatedBytes = get(generatedClassName, bytes); + + if (protectionDomain == null) { + defineClass(loader, generatedClassName, generatedBytes); + } else { + defineClass(loader, generatedClassName, generatedBytes, + protectionDomain); + } + + } + + } + + private String[] getGeneratedClassesNames(String className, byte[] bytes) { + String key = generateKey(className, bytes); + + byte[] readBytes = generatedCache.get(key); + if (readBytes == null) { + return null; + } + String readString = new String(readBytes); + return readString.split(GENERATED_CACHE_SEPARATOR); + } + + public void addGeneratedClassesNames(String parentClassName, byte[] parentBytes, String generatedClassName) { + if (!enabled) { + return; + } + String key = generateKey(parentClassName, parentBytes); + + byte[] storedBytes = generatedCache.get(key); + if (storedBytes == null) { + generatedCache.put(key, generatedClassName.getBytes()); + } else { + String storedClasses = new String(storedBytes); + storedClasses += GENERATED_CACHE_SEPARATOR + generatedClassName; + generatedCache.put(key, storedClasses.getBytes()); + } + } + + private Method defineClassMethod = null; + private Method defineClassWithProtectionDomainMethod = null; + + private void defineClass(ClassLoader loader, String name, byte[] bytes) { + + Object clazz = null; + + 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 (InvocationTargetException e) { + if (e.getTargetException() instanceof LinkageError) { + e.printStackTrace(); + } else { + System.out.println("define generated class failed" + + e.getTargetException()); + } + } catch (Exception e) { + e.printStackTrace(); + Dump.dumpWithException(e); + } + } + + private void defineClass(ClassLoader loader, String name, byte[] bytes, + ProtectionDomain protectionDomain) { + + Object clazz = null; + + try { + // System.out.println(">> Defining with protection domain " + name + + // " pd=" + protectionDomain); + if (defineClassWithProtectionDomainMethod == null) { + defineClassWithProtectionDomainMethod = ClassLoader.class + .getDeclaredMethod("defineClass", new Class[] { + String.class, bytes.getClass(), int.class, + int.class, ProtectionDomain.class }); + } + defineClassWithProtectionDomainMethod.setAccessible(true); + clazz = defineClassWithProtectionDomainMethod.invoke(loader, + new Object[] { name, bytes, Integer.valueOf(0), + new Integer(bytes.length), protectionDomain }); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof LinkageError) { + e.printStackTrace(); + // is already defined (happens for X$ajcMightHaveAspect + // interfaces since aspects are reweaved) + // TODO maw I don't think this is OK and + } else { + e.printStackTrace(); + } + }catch (NullPointerException e) { + System.out.println("NullPointerException loading class:"+name+". Probabily caused by a corruput cache. Please clean it and reboot the server"); + } catch (Exception e) { + e.printStackTrace(); + Dump.dumpWithException(e); + } + + } + +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java new file mode 100644 index 000000000..49569dd0e --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; + +import org.aspectj.weaver.Dump; + +public class SimpleCacheFactory { + + public static final String CACHE_ENABLED_PROPERTY = "aj.weaving.cache.enabled"; + public static final String CACHE_DIR = "aj.weaving.cache.dir"; + public static final String CACHE_IMPL = "aj.weaving.cache.impl"; + + public static final String PATH_DEFAULT= "/tmp/"; // TODO windows default...? + public static final boolean BYDEFAULT= false; + + + public static String path = PATH_DEFAULT; + public static Boolean enabled = false; + private static boolean determinedIfEnabled = false; + private static SimpleCache lacache=null; + + public static synchronized SimpleCache createSimpleCache(){ + if (lacache==null){ + if (!determinedIfEnabled) { + determineIfEnabled(); + } + + if (!enabled) { + return null; + } + + try { + path = System.getProperty(CACHE_DIR); + if (path == null){ + path = PATH_DEFAULT; + } + + } catch (Throwable t) { + path=PATH_DEFAULT; + t.printStackTrace(); + Dump.dumpWithException(t); + } + File f = new File(path); + if (!f.exists()){ + f.mkdir(); + } + lacache= new SimpleCache(path, enabled); + } + return lacache; + + } + + private static void determineIfEnabled() { + try { + String property = System.getProperty(CACHE_ENABLED_PROPERTY); + if (property == null ){ + enabled = BYDEFAULT; + } + else if (property.equalsIgnoreCase("true")){ + + String impl = System.getProperty(CACHE_IMPL); + if (SimpleCache.IMPL_NAME.equals(impl)){ + enabled = true; + } + else{ + enabled = BYDEFAULT; + } + } + else{ + enabled = BYDEFAULT; + } + + } catch (Throwable t) { + enabled=BYDEFAULT; + System.err.println("Error creating cache"); + t.printStackTrace(); + Dump.dumpWithException(t); + } + determinedIfEnabled = true; + } + + // Should behave ok with two threads going through here, well whoever gets there first will set determinedIfEnabled but only after + // it has set 'enabled' to the right value. + public static boolean isEnabled() { + if (!determinedIfEnabled) { + determineIfEnabled(); + } + return enabled; + } + + +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java b/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java index 6888c6c23..b281d412f 100644 --- a/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java +++ b/weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java @@ -68,6 +68,7 @@ import java.util.List; */ 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; @@ -171,7 +172,9 @@ public class WeavedClassCache { * @return true if caching is enabled */ public static boolean isEnabled() { - return System.getProperty(WEAVED_CLASS_CACHE_ENABLED) != null; + String enabled = System.getProperty(WEAVED_CLASS_CACHE_ENABLED); + String impl = System.getProperty(CACHE_IMPL); + return (enabled != null && (impl == null || !SimpleCache.IMPL_NAME.equalsIgnoreCase(impl) ) ); } /** diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java index a87b1eba3..861f637fc 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java @@ -20,6 +20,7 @@ import junit.framework.TestSuite; public class CacheTests { public static Test suite() { TestSuite suite = new TestSuite(CacheTests.class.getName()); + suite.addTestSuite(SimpleClassCacheTest.class); suite.addTestSuite(WeavedClassCacheTest.class); suite.addTestSuite(DefaultCacheKeyResolverTest.class); suite.addTestSuite(DefaultFileCacheBackingTest.class); diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java new file mode 100644 index 000000000..0624d0ff0 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado (lucierna) initial implementation + ********************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import junit.framework.TestCase; +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.tools.GeneratedClassHandler; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + */ +public class SimpleClassCacheTest extends TestCase { + byte[] FAKE_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] FAKE_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + byte[] FAKE_WOVEN_BYTES_V1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; + byte[] FAKE_WOVEN_BYTES_V2 = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}; + + + private SimpleCache createCache() throws Exception { + return new SimpleCache(System.getProperty("java.io.tmpdir"),true); + } + + + public void testCache() throws Exception { + String classA = "com.generated.A"; + SimpleCache cache = createCache(); + + cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); + + + // Test the returned woven bytes are the original one + byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); + } + + // Assure the class is properly backed up in the backing folder + File f = new File (System.getProperty("java.io.tmpdir") + File.separator + "com.generated.A-1164760902"); + assertTrue("Class should be backed up to backing folder, with te CRC:1164760902 ",f.exists()); + + } + + public void testDifferentVersionCache() throws Exception { + String classA = "com.generated.A"; + SimpleCache cache = createCache(); + cache.put(classA, FAKE_BYTES_V1, FAKE_WOVEN_BYTES_V1); + cache.put(classA, FAKE_BYTES_V2, FAKE_WOVEN_BYTES_V2); + + // Test the returned woven bytes are the original one for v1 + byte result[] = cache.getAndInitialize(classA, FAKE_BYTES_V1, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version v1 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V1[i]); + } + + // Test the returned woven bytes are the original one for v2 + result = cache.getAndInitialize(classA, FAKE_BYTES_V2, null, null); + for(int i = 0; i < result.length; i ++){ + assertEquals("Cached version v2 byte[" +i+"] should be equal to the original woven classe",result[i],FAKE_WOVEN_BYTES_V2[i]); + } + } +}
\ No newline at end of file |