From 8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Mon, 1 Oct 2012 15:09:15 -0700 Subject: [PATCH] 386341 --- .../src/org/aspectj/weaver/loadtime/Aj.java | 33 +- .../org/aspectj/tools/ajc/AjcTestCase.java | 100 ++++- .../newsrc/org/aspectj/testing/RunSpec.java | 16 +- tests/ajcTestSuite.dtd | 1 + tests/features171/pr386341/A.java | 29 ++ tests/features171/pr386341/X.aj | 20 + tests/features171/pr386341/aop.xml | 10 + .../systemtest/ajc171/AllTestsAspectJ171.java | 3 + .../systemtest/ajc171/NewFeatures.java | 71 ++++ .../systemtest/ajc171/newfeatures-tests.xml | 41 ++ .../aspectj/weaver/tools/WeavingAdaptor.java | 9 + .../tools/cache/AbstractCacheBacking.java | 45 +++ .../tools/cache/AbstractFileCacheBacking.java | 86 ++++ .../AbstractIndexedFileCacheBacking.java | 244 ++++++++++++ .../weaver/tools/cache/SimpleCache.java | 377 ++++++++++++++++++ .../tools/cache/SimpleCacheFactory.java | 104 +++++ .../weaver/tools/cache/WeavedClassCache.java | 5 +- .../weaver/tools/cache/CacheTests.java | 1 + .../tools/cache/SimpleClassCacheTest.java | 79 ++++ 19 files changed, 1253 insertions(+), 21 deletions(-) create mode 100644 tests/features171/pr386341/A.java create mode 100644 tests/features171/pr386341/X.aj create mode 100644 tests/features171/pr386341/aop.xml create mode 100644 tests/src/org/aspectj/systemtest/ajc171/NewFeatures.java create mode 100644 tests/src/org/aspectj/systemtest/ajc171/newfeatures-tests.xml create mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractCacheBacking.java create mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java create mode 100644 weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java create mode 100644 weaver/src/org/aspectj/weaver/tools/cache/SimpleCache.java create mode 100644 weaver/src/org/aspectj/weaver/tools/cache/SimpleCacheFactory.java create mode 100644 weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java 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. *

@@ -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 ListtokenizeCommand(String command) { + StringTokenizer st = new StringTokenizer(command," ", false); + ArrayList arguments = new ArrayList(); + 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 @@ + 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 @@ + + + + + + + + + + \ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 "common" {@link CacheBacking} implementation + */ +public abstract class AbstractCacheBacking implements CacheBacking { + protected final Trace logger=TraceFactory.getTraceFactory().getTrace(getClass()); + + protected AbstractCacheBacking () { + super(); + } + + /** + * Calculates CRC32 on the provided bytes + * @param bytes The bytes array - ignored if null/empty + * @return Calculated CRC + * @see {@link CRC32} + */ + public static final long crc (byte[] bytes) { + if ((bytes == null) || (bytes.length <= 0)) { + return 0L; + } + + CRC32 crc32=new CRC32(); + crc32.update(bytes); + return crc32.getValue(); + } +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java new file mode 100644 index 000000000..5447c158b --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/AbstractFileCacheBacking.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Useful "common" functionality for caching to files + */ +public abstract class AbstractFileCacheBacking extends AbstractCacheBacking { + /** + * Default property used to specify a default weaving cache dir location + */ + public static final String WEAVED_CLASS_CACHE_DIR = "aj.weaving.cache.dir"; + private final File cacheDirectory; + + protected AbstractFileCacheBacking (File cacheDirectory) { + if ((this.cacheDirectory=cacheDirectory) == null) { + throw new IllegalStateException("No cache directory specified"); + } + } + + public File getCacheDirectory () { + return cacheDirectory; + } + + protected void writeClassBytes (String key, byte[] bytes) throws Exception { + File dir=getCacheDirectory(), file=new File(dir, key); + FileOutputStream out=new FileOutputStream(file); + try { + out.write(bytes); + } finally { + close(out, file); + } + } + + protected void delete(File file) { + if (file.exists() && (!file.delete())) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Error deleting file " + file.getAbsolutePath()); + } + } + } + + protected void close(OutputStream out, File file) { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close write file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } + + protected void close(InputStream in, File file) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + if ((logger != null) && logger.isTraceEnabled()) { + logger.error("Failed (" + e.getClass().getSimpleName() + ")" + + " to close read file " + file.getAbsolutePath() + + ": " + e.getMessage(), e); + } + } + } + } +} diff --git a/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java b/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java new file mode 100644 index 000000000..6653c9bdf --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/cache/AbstractIndexedFileCacheBacking.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Kew (vmware) initial implementation + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StreamCorruptedException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.LangUtil; + +/** + * Uses an index 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 index=getIndex(); + if ((index == null) || index.isEmpty()) { + return EMPTY_KEYS; + } + Collection matches=new LinkedList(); + 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 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 getIndex (); + + protected Map readIndex (File cacheDir, File cacheFile) { + Map indexMap=new TreeMap(); + 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 index) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(index) ? Collections.emptyList() : index.values()); + } + + protected void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries)); + } + + protected void writeIndex (File indexFile, Collection 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 cacheMap; + private boolean enabled = false; + + // cache for generated classes + private Map 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 -- 2.39.5