]> source.dussan.org Git - aspectj.git/commitdiff
386341
authorAndy Clement <andrew.clement@gmail.com>
Mon, 1 Oct 2012 22:09:15 +0000 (15:09 -0700)
committerAndy Clement <andrew.clement@gmail.com>
Mon, 1 Oct 2012 22:09:15 +0000 (15:09 -0700)
19 files changed:
loadtime/src/org/aspectj/weaver/loadtime/Aj.java
org.aspectj.ajdt.core/testsrc/org/aspectj/tools/ajc/AjcTestCase.java
testing/newsrc/org/aspectj/testing/RunSpec.java
tests/ajcTestSuite.dtd
tests/features171/pr386341/A.java [new file with mode: 0644]
tests/features171/pr386341/X.aj [new file with mode: 0644]
tests/features171/pr386341/aop.xml [new file with mode: 0644]
tests/src/org/aspectj/systemtest/ajc171/AllTestsAspectJ171.java
tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java [new file with mode: 0644]
tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java
weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java [new file with mode: 0644]
weaver/src/org/aspectj/weaver/tools/cache/WeavedClassCache.java
weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java
weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java [new file with mode: 0644]

index 3582da8710e2cc8e5d3a33a3e63259bedbcd45de..f518e4802e5b2882b054bcc9507f1cd46b5630fe 100644 (file)
@@ -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;
index d5ec93e6682b8d80efef1b9eb64f5d1410a2cda8..c8bb6c1b6bcfdbd5d267b485eebfa0da871de73b 100644 (file)
@@ -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));
        }
+
+
 }
index 7a58425486be92e593bbc02cb6b576d4004c957f..8a2fea8b389b2c69f54f07f822a2cf0fb5a411cc 100644 (file)
@@ -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);
index 44734a9ccb71cd789d8026c09d84a8edfb76a0a0..fbfcd011a426112bc7ec9a42ea3572b0f1afac18 100644 (file)
@@ -44,6 +44,7 @@
    <!ATTLIST run aspectpath CDATA #IMPLIED >\r
    <!ATTLIST run classpath CDATA #IMPLIED >\r
    <!ATTLIST run LTW CDATA #IMPLIED >\r
+   <!ATTLIST run usefullltw CDATA #IMPLIED >\r
    <!ATTLIST run exception CDATA #IMPLIED >\r
 \r
    <!ELEMENT message (source*)>\r
diff --git a/tests/features171/pr386341/A.java b/tests/features171/pr386341/A.java
new file mode 100644 (file)
index 0000000..98bbf4f
--- /dev/null
@@ -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 (file)
index 0000000..b96b73a
--- /dev/null
@@ -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 (file)
index 0000000..33e509e
--- /dev/null
@@ -0,0 +1,10 @@
+<aspectj>
+       <aspects>
+               <aspect name="X"/>
+       </aspects>
+
+       <weaver options="">
+               <include within="*"/>
+       </weaver>
+       
+</aspectj>
\ No newline at end of file
index 78e321b848a6c2a122c1750db9629e14dda79970..f9d309794748ebe10995d7219c51fec8c46ff862 100644 (file)
@@ -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 (file)
index 0000000..79d1a46
--- /dev/null
@@ -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 (file)
index 0000000..32f0f18
--- /dev/null
@@ -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
index 8b31ce5861846e9a91db342601b02f8751fc1f1e..48ef6ae8a6e5a04a639ad838359cadeabad1280e 100644 (file)
@@ -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 (file)
index 0000000..649e21d
--- /dev/null
@@ -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 (file)
index 0000000..5447c15
--- /dev/null
@@ -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 (file)
index 0000000..6653c9b
--- /dev/null
@@ -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 (file)
index 0000000..45d718a
--- /dev/null
@@ -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 (file)
index 0000000..49569dd
--- /dev/null
@@ -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;
+       } 
+       
+
+}
index 6888c6c2350b53cc445c73022655a3d59a0c5257..b281d412ff70f8eb62f56dc080e164b7d329fe4e 100644 (file)
@@ -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) ) );
        }
 
        /**
index a87b1eba3f0ebcfe92c953c8225b8eabea9a3d63..861f637fcd7847af83bd1faec8fe21a86d5ffa1f 100644 (file)
@@ -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 (file)
index 0000000..0624d0f
--- /dev/null
@@ -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