aboutsummaryrefslogtreecommitdiffstats
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
parent9a3cc2bc5c824d252140fb3d1e2e27f2163e6d53 (diff)
downloadaspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.tar.gz
aspectj-8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917.zip
386341
-rw-r--r--loadtime/src/org/aspectj/weaver/loadtime/Aj.java33
-rw-r--r--org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java100
-rw-r--r--testing/newsrc/org/aspectj/testing/RunSpec.java16
-rw-r--r--tests/ajcTestSuite.dtd1
-rw-r--r--tests/features171/pr386341/A.java29
-rw-r--r--tests/features171/pr386341/X.aj20
-rw-r--r--tests/features171/pr386341/aop.xml10
-rw-r--r--tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java3
-rw-r--r--tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java71
-rw-r--r--tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml41
-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
19 files changed, 1253 insertions, 21 deletions
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/Aj.java b/loadtime/src/org/aspectj/weaver/loadtime/Aj.java
index 3582da871..f518e4802 100644
--- a/loadtime/src/org/aspectj/weaver/loadtime/Aj.java
+++ b/loadtime/src/org/aspectj/weaver/loadtime/Aj.java
@@ -17,6 +17,7 @@ import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -25,6 +26,8 @@ import org.aspectj.weaver.Dump;
import org.aspectj.weaver.tools.Trace;
import org.aspectj.weaver.tools.TraceFactory;
import org.aspectj.weaver.tools.WeavingAdaptor;
+import org.aspectj.weaver.tools.cache.SimpleCache;
+import org.aspectj.weaver.tools.cache.SimpleCacheFactory;
/**
* Adapter between the generic class pre processor interface and the AspectJ weaver Load time weaving consistency relies on
@@ -35,6 +38,7 @@ import org.aspectj.weaver.tools.WeavingAdaptor;
public class Aj implements ClassPreProcessor {
private IWeavingContext weavingContext;
+ public static SimpleCache laCache=SimpleCacheFactory.createSimpleCache();
/**
* References are added to this queue when their associated classloader is removed, and once on here that indicates that we
@@ -88,6 +92,14 @@ public class Aj implements ClassPreProcessor {
try {
synchronized (loader) {
+
+ if (SimpleCacheFactory.isEnabled()) {
+ byte[] cacheBytes= laCache.getAndInitialize(className, bytes,loader,protectionDomain);
+ if (cacheBytes!=null){
+ return cacheBytes;
+ }
+ }
+
WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(loader, weavingContext);
if (weavingAdaptor == null) {
if (trace.isTraceEnabled())
@@ -100,6 +112,9 @@ public class Aj implements ClassPreProcessor {
Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true);
if (trace.isTraceEnabled())
trace.exit("preProcess", newBytes);
+ if (SimpleCacheFactory.isEnabled()) {
+ laCache.put(className, bytes, newBytes);
+ }
return newBytes;
} finally {
weavingAdaptor.setActiveProtectionDomain(null);
@@ -247,21 +262,35 @@ public class Aj implements ClassPreProcessor {
synchronized (weavingAdaptors) {
checkQ();
- adaptor = (ExplicitlyInitializedClassLoaderWeavingAdaptor) weavingAdaptors.get(adaptorKey);
+ if(loader.equals(myClassLoader)){
+ adaptor = myClassLoaderAdpator;
+ }
+ else{
+ adaptor = (ExplicitlyInitializedClassLoaderWeavingAdaptor) weavingAdaptors.get(adaptorKey);
+ }
if (adaptor == null) {
// create it and put it back in the weavingAdaptors map but avoid any kind of instantiation
// within the synchronized block
ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor();
adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor);
- weavingAdaptors.put(adaptorKey, adaptor);
+ if(myClassLoaderAdpator == null){
+ myClassLoaderAdpator = adaptor;
+ }
+ else{
+ weavingAdaptors.put(adaptorKey, adaptor);
+ }
}
}
// perform the initialization
return adaptor.getWeavingAdaptor(loader, weavingContext);
+
}
+ private static final ClassLoader myClassLoader = WeavingAdaptor.class.getClassLoader();
+ private static ExplicitlyInitializedClassLoaderWeavingAdaptor myClassLoaderAdpator;
}
+
static class ExplicitlyInitializedClassLoaderWeavingAdaptor {
private final ClassLoaderWeavingAdaptor weavingAdaptor;
private boolean isInitialized;
diff --git a/org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java b/org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java
index d5ec93e66..c8bb6c1b6 100644
--- a/org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java
+++ b/org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java
@@ -7,30 +7,27 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- * Adrian Colyer,
+ * Adrian Colyer, Abraham Nevado (lucierna)
* ******************************************************************/
package org.aspectj.tools.ajc;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FilePermission;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
+import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.ReflectPermission;
import java.net.URL;
import java.net.URLClassLoader;
-import java.security.Permission;
-import java.security.Policy;
-import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.PropertyPermission;
import java.util.StringTokenizer;
import junit.framework.TestCase;
@@ -126,6 +123,8 @@ public class AjcTestCase extends TestCase {
public final static boolean DEFAULT_ERR_VERBOSE = getBoolean("org.aspectj.tools.ajc.AjcTestCase.verbose.err", DEFAULT_VERBOSE);
public final static boolean DEFAULT_OUT_VERBOSE = getBoolean("org.aspectj.tools.ajc.AjcTestCase.verbose.out", DEFAULT_VERBOSE);
+ private Process exec;
+
/**
* Helper class that represents the specification of an individual message expected to be produced during a compilation run.
* <p>
@@ -564,7 +563,7 @@ public class AjcTestCase extends TestCase {
}
public RunResult run(String className, String[] args, String classpath) {
- return run(className, args, null, false);
+ return run(className, args, "", null, false,false);
}
/**
@@ -574,8 +573,10 @@ public class AjcTestCase extends TestCase {
* @param args the arguments to pass to the program.
* @param classpath the execution classpath, the sandbox directory, runtime, testing-client, bridge, and util projects will all
* be appended to the classpath, as will any jars in the sandbox.
+ * @param runSpec
*/
- public RunResult run(String className, String[] args, final String classpath, boolean useLTW) {
+ public RunResult run(String className, String[] args, String vmargs, final String classpath, boolean useLTW, boolean useFullLTW) {
+
if (args != null) {
for (int i = 0; i < args.length; i++) {
args[i] = substituteSandbox(args[i]);
@@ -595,9 +596,10 @@ public class AjcTestCase extends TestCase {
URLClassLoader testLoader = (URLClassLoader) getClass().getClassLoader();
ClassLoader parentLoader = testLoader.getParent();
+
+
/* Sandbox -> AspectJ -> Extension -> Bootstrap */
- if (useLTW) {
-
+ if ( !useFullLTW && useLTW) {
/*
* Create a new AspectJ class loader using the existing test CLASSPATH and any missing Java 5 projects
*/
@@ -611,14 +613,41 @@ public class AjcTestCase extends TestCase {
URL[] sandboxUrls = getURLs(cp.toString());
sandboxLoader = createWeavingClassLoader(sandboxUrls, aspectjLoader);
// sandboxLoader = createWeavingClassLoader(sandboxUrls,testLoader);
- }
+ }else if(useFullLTW && useLTW) {
+ if(vmargs == null){
+ vmargs ="";
+ }
+
+ File directory = new File (".");
+ String absPath = directory.getAbsolutePath();
+ String javaagent= absPath+File.separator+".."+File.separator+"aj-build"+File.separator+"dist"+File.separator+"tools"+File.separator+"lib"+File.separator+"aspectjweaver.jar";
+ try {
- /* Sandbox + AspectJ -> Extension -> Bootstrap */
- else {
+ String command ="java " +vmargs+ " -classpath " + cp +" -javaagent:"+javaagent + " " + className ;
+
+ // Command is executed using ProcessBuilder to allow setting CWD for ajc sandbox compliance
+ ProcessBuilder pb = new ProcessBuilder(tokenizeCommand(command));
+ pb.directory( new File(ajc.getSandboxDirectory().getAbsolutePath()));
+ exec = pb.start();
+ BufferedReader stdInput = new BufferedReader(new InputStreamReader(exec.getInputStream()));
+ BufferedReader stdError = new BufferedReader(new InputStreamReader(exec.getErrorStream()));
+ exec.waitFor();
+ lastRunResult = createResultFromBufferReaders(command,stdInput, stdError);
+ } catch (Exception e) {
+ System.out.println("Error executing full LTW test: " + e);
+ e.printStackTrace();
+ }
+
+ return lastRunResult;
+
+ }else{
cp.append(DEFAULT_CLASSPATH_ENTRIES);
URL[] urls = getURLs(cp.toString());
sandboxLoader = new URLClassLoader(urls, parentLoader);
}
+ ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
+
StringBuffer command = new StringBuffer("java -classpath ");
command.append(cp.toString());
@@ -636,8 +665,7 @@ public class AjcTestCase extends TestCase {
// } catch (SecurityException se) {
// // SecurityManager already set
// }
- ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
- ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
+
ClassLoader contexClassLoader = Thread.currentThread().getContextClassLoader();
try {
try {
@@ -683,6 +711,44 @@ public class AjcTestCase extends TestCase {
return lastRunResult;
}
+ private List<String >tokenizeCommand(String command) {
+ StringTokenizer st = new StringTokenizer(command," ", false);
+ ArrayList<String> arguments = new ArrayList<String>();
+ while(st.hasMoreElements()){
+ String nextToken =st.nextToken();
+ arguments.add(nextToken);
+ }
+
+ return arguments;
+ }
+
+ private RunResult createResultFromBufferReaders(String command,
+ BufferedReader stdInput, BufferedReader stdError) throws IOException {
+ String line = "";
+ ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
+ ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
+
+ PrintWriter stdOutWriter = new PrintWriter(baosOut);
+ PrintWriter stdErrWriter = new PrintWriter(baosErr);
+
+ while ((line = stdInput.readLine()) != null) {
+ stdOutWriter.println(line);
+ System.out.println(line);
+ }
+ stdOutWriter.flush();
+ while ((line = stdError.readLine()) != null) {
+ stdErrWriter.println(line);
+ System.err.println(line);
+
+ }
+ stdErrWriter.flush();
+
+ baosOut.close();
+ baosErr.close();
+
+ return new RunResult(command.toString(), new String(baosOut.toByteArray()), new String(baosErr.toByteArray()));
+ }
+
// static class MyPolicy extends Policy {
//
// @Override
@@ -928,4 +994,6 @@ public class AjcTestCase extends TestCase {
delegatingOut = new DelegatingOutputStream(out);
System.setOut(new PrintStream(delegatingOut));
}
+
+
}
diff --git a/testing/newsrc/org/aspectj/testing/RunSpec.java b/testing/newsrc/org/aspectj/testing/RunSpec.java
index 7a5842548..8a2fea8b3 100644
--- a/testing/newsrc/org/aspectj/testing/RunSpec.java
+++ b/testing/newsrc/org/aspectj/testing/RunSpec.java
@@ -7,7 +7,7 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- * Adrian Colyer,
+ * Adrian Colyer, Abraham Nevado (lucierna)
* ******************************************************************/
package org.aspectj.testing;
@@ -39,6 +39,7 @@ public class RunSpec implements ITestStep {
private String ltwFile;
private String xlintFile;
private String vmargs;
+ private String usefullltw;
public RunSpec() {
}
@@ -50,11 +51,12 @@ public class RunSpec implements ITestStep {
String[] args = buildArgs();
// System.err.println("? execute() inTestCase='" + inTestCase + "', ltwFile=" + ltwFile);
boolean useLtw = copyLtwFile(inTestCase.getSandboxDirectory());
+
copyXlintFile(inTestCase.getSandboxDirectory());
try {
setSystemProperty("test.base.dir", inTestCase.getSandboxDirectory().getAbsolutePath());
- AjcTestCase.RunResult rr = inTestCase.run(getClassToRun(), args, getClasspath(), useLtw);
+ AjcTestCase.RunResult rr = inTestCase.run(getClassToRun(), args, vmargs, getClasspath(), useLtw, "true".equalsIgnoreCase(usefullltw));
if (stdErrSpec != null) {
stdErrSpec.matchAgainst(rr.getStdErr(), orderedStderr);
@@ -211,6 +213,16 @@ public class RunSpec implements ITestStep {
return vmargs;
}
+
+ public String getUsefullltw() {
+ return usefullltw;
+ }
+
+ public void setUsefullltw(String usefullltw) {
+ this.usefullltw = usefullltw;
+ }
+
+
private void copyXlintFile(File sandboxDirectory) {
if (xlintFile != null) {
File from = new File(baseDir, xlintFile);
diff --git a/tests/ajcTestSuite.dtd b/tests/ajcTestSuite.dtd
index 44734a9cc..fbfcd011a 100644
--- a/tests/ajcTestSuite.dtd
+++ b/tests/ajcTestSuite.dtd
@@ -44,6 +44,7 @@
<!ATTLIST run aspectpath CDATA #IMPLIED >
<!ATTLIST run classpath CDATA #IMPLIED >
<!ATTLIST run LTW CDATA #IMPLIED >
+ <!ATTLIST run usefullltw CDATA #IMPLIED >
<!ATTLIST run exception CDATA #IMPLIED >
<!ELEMENT message (source*)>
diff --git a/tests/features171/pr386341/A.java b/tests/features171/pr386341/A.java
new file mode 100644
index 000000000..98bbf4fb4
--- /dev/null
+++ b/tests/features171/pr386341/A.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andy Clement - Repro test case
+ * Abraham Nevado
+ *******************************************************************************/
+
+class AtomicAction {
+ int status() { return 1; }
+ int commit(int n) { return 1; }
+}
+
+public class A {
+ public static void main(String []argv) throws Exception {
+ A a = new A();
+ a.m();
+ }
+
+ public void m() throws Exception{
+ Thread.sleep(5*1000);
+ }
+
+}
+
diff --git a/tests/features171/pr386341/X.aj b/tests/features171/pr386341/X.aj
new file mode 100644
index 000000000..b96b73ad1
--- /dev/null
+++ b/tests/features171/pr386341/X.aj
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andy Clement - Repro test case
+ * Abraham Nevado
+ *******************************************************************************/
+
+aspect X {
+ after(): execution(* *.*()) {
+ System.out.println("It Worked-after");
+ }
+ before(): execution(* *.*()) {
+ System.out.println("It Worked-before");
+ }
+}
diff --git a/tests/features171/pr386341/aop.xml b/tests/features171/pr386341/aop.xml
new file mode 100644
index 000000000..33e509edb
--- /dev/null
+++ b/tests/features171/pr386341/aop.xml
@@ -0,0 +1,10 @@
+<aspectj>
+ <aspects>
+ <aspect name="X"/>
+ </aspects>
+
+ <weaver options="">
+ <include within="*"/>
+ </weaver>
+
+</aspectj> \ No newline at end of file
diff --git a/tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java b/tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java
index 78e321b84..f9d309794 100644
--- a/tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java
+++ b/tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.aspectj.systemtest.ajc171;
+import org.aspectj.systemtest.ajc1610.NewFeatures;
+
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -19,6 +21,7 @@ public class AllTestsAspectJ171 {
TestSuite suite = new TestSuite("AspectJ 1.7.1 tests");
// $JUnit-BEGIN$
suite.addTest(Ajc171Tests.suite());
+ suite.addTest(NewFeatures.suite());
// $JUnit-END$
return suite;
}
diff --git a/tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java b/tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java
new file mode 100644
index 000000000..79d1a4669
--- /dev/null
+++ b/tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Lucierna
+ * 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:
+ * Abraham Nevado (lucierna) - initial implementation
+ *******************************************************************************/
+package org.aspectj.systemtest.ajc171;
+
+import java.io.File;
+
+import junit.framework.Test;
+
+import org.aspectj.apache.bcel.classfile.JavaClass;
+import org.aspectj.testing.XMLBasedAjcTestCase;
+
+// NOTE THIS IS ACTUALLY IN 1.7.2 - IT IS JUST THAT THE PATCH WAS CREATED AGAINST 1.7.1
+public class NewFeatures extends org.aspectj.testing.XMLBasedAjcTestCase {
+
+ public void testSharedCache() {
+ this.runTest("Test Shared Cache");
+ File cacheFolder = new File(ajc.getSandboxDirectory().getAbsolutePath() + File.separator + "panenka.cache");
+ assertTrue("Cache folder should be written when using share cache", cacheFolder.exists());
+ //Delete the cache from the ajc sandbox
+ deleteFolder(cacheFolder);
+ }
+
+ public void testPerClassLoaderCache() {
+ this.runTest("Test Per ClassLoader Cache");
+ File cacheFolder = new File(ajc.getSandboxDirectory().getAbsolutePath() + File.separator + "panenka.cache");
+ assertFalse("Shared Cache Folder should not be present", cacheFolder.exists());
+ }
+
+ public void testDefaultCachePerClassloader() {
+ this.runTest("Test Default Cache Per ClassLoader");
+ File cacheFolder = new File(ajc.getSandboxDirectory().getAbsolutePath() + File.separator + "panenka.cache");
+ assertFalse("By Default Per ClassLoader Cache should be used and not the shared one", cacheFolder.exists());
+ }
+
+ // ///////////////////////////////////////
+
+ private static void deleteFolder(File folder) {
+ File[] files = folder.listFiles();
+ if(files!=null) { //some JVMs return null for empty dirs
+ for(File f: files) {
+ if(f.isDirectory()) {
+ deleteFolder(f);
+ } else {
+ f.delete();
+ }
+ }
+ }
+ folder.delete();
+ }
+
+ public static Test suite() {
+ return XMLBasedAjcTestCase.loadSuite(NewFeatures.class);
+ }
+
+ private JavaClass getMyClass(String className) throws ClassNotFoundException {
+ return getClassFrom(ajc.getSandboxDirectory(), className);
+ }
+
+ protected File getSpecFile() {
+ return new File("../tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml");
+ }
+
+} \ No newline at end of file
diff --git a/tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml b/tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml
new file mode 100644
index 000000000..32f0f1845
--- /dev/null
+++ b/tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml
@@ -0,0 +1,41 @@
+<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"[]>
+
+<!-- AspectJ v1.7.1 Features Tests -->
+<suite>
+ <ajc-test dir="features171/pr386341" title="Test Shared Cache">
+ <compile files="A.java" options="-1.5"/>
+ <compile files="X.aj" options="-1.5 -Xlint:ignore" />
+ <run class="A" ltw="aop.xml" usefullltw="true" vmargs="-Daj.weaving.cache.enabled=true -Daj.weaving.cache.dir=./ -Daj.weaving.cache.impl=shared" >
+ <stdout>
+ <line text="It Worked-before"/>
+ <line text="It Worked-after"/>
+ </stdout>
+ </run>
+ </ajc-test>
+
+ <ajc-test dir="features171/pr386341" title="Test Per ClassLoader Cache">
+ <compile files="A.java" options="-1.5"/>
+ <compile files="X.aj" options="-1.5 -Xlint:ignore" />
+ <run class="A" ltw="aop.xml" usefullltw="true" vmargs="-Daj.weaving.cache.enabled=true -Daj.weaving.cache.dir=./ -Daj.weaving.cache.impl=perloader" >
+ <stdout>
+ <line text="It Worked-before"/>
+ <line text="It Worked-after"/>
+ </stdout>
+ </run>
+ </ajc-test>
+
+ <ajc-test dir="features171/pr386341" title="Test Default Cache Per ClassLoader">
+ <compile files="A.java" options="-1.5"/>
+ <compile files="X.aj" options="-1.5 -Xlint:ignore" />
+ <run class="A" ltw="aop.xml" usefullltw="true" vmargs="-Daj.weaving.cache.enabled=true -Daj.weaving.cache.dir=./" >
+ <stdout>
+ <line text="It Worked-before"/>
+ <line text="It Worked-after"/>
+ </stdout>
+ </run>
+ </ajc-test>
+
+
+
+
+</suite> \ No newline at end of file
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