summaryrefslogtreecommitdiffstats
path: root/weaver
diff options
context:
space:
mode:
authorAndy Clement <andrew.clement@gmail.com>2012-10-01 15:09:15 -0700
committerAndy Clement <andrew.clement@gmail.com>2012-10-01 15:09:15 -0700
commit8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917 (patch)
treeeb9e96e988a63e1aa48896b279adae9bc41f9084 /weaver
parent9a3cc2bc5c824d252140fb3d1e2e27f2163e6d53 (diff)
downloadaspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.tar.gz
aspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.zip
386341
Diffstat (limited to 'weaver')
-rw-r--r--weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java9
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java45
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java86
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java244
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java377
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java104
-rw-r--r--weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java5
-rw-r--r--weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java1
-rw-r--r--weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java79
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 &quot;common&quot; {@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 &quot;common&quot; 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