You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SimpleCache.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. package org.aspectj.weaver.tools.cache;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.FileOutputStream;
  7. import java.io.IOException;
  8. import java.io.ObjectInputStream;
  9. import java.io.ObjectOutputStream;
  10. import java.lang.reflect.InvocationTargetException;
  11. import java.lang.reflect.Method;
  12. import java.security.ProtectionDomain;
  13. import java.util.Arrays;
  14. import java.util.Collections;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import java.util.Optional;
  18. import java.util.zip.CRC32;
  19. import org.aspectj.weaver.Dump;
  20. import org.aspectj.weaver.tools.Trace;
  21. import org.aspectj.weaver.tools.TraceFactory;
  22. /*******************************************************************************
  23. * Copyright (c) 2012 Contributors.
  24. * All rights reserved.
  25. * This program and the accompanying materials are made available
  26. * under the terms of the Eclipse Public License v 2.0
  27. * which accompanies this distribution and is available at
  28. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  29. *
  30. * Contributors:
  31. * Abraham Nevado (lucierna) initial implementation
  32. ********************************************************************************/
  33. public class SimpleCache {
  34. private static final String SAME_BYTES_STRING = "IDEM";
  35. static final byte[] SAME_BYTES = SAME_BYTES_STRING.getBytes();
  36. private Map<String, byte[]> cacheMap;
  37. private boolean enabled = false;
  38. // cache for generated classes
  39. private Map<String, byte[]> generatedCache;
  40. private static final String GENERATED_CACHE_SUBFOLDER = "panenka.cache";
  41. private static final String GENERATED_CACHE_SEPARATOR = ";";
  42. public static final String IMPL_NAME = "shared";
  43. protected SimpleCache(String folder, boolean enabled) {
  44. this.enabled = enabled;
  45. cacheMap = Collections.synchronizedMap(StoreableCachingMap.init(folder));
  46. if (enabled) {
  47. String generatedCachePath = folder + File.separator + GENERATED_CACHE_SUBFOLDER;
  48. File f = new File (generatedCachePath);
  49. if (!f.exists()){
  50. f.mkdir();
  51. }
  52. generatedCache = Collections.synchronizedMap(StoreableCachingMap.init(generatedCachePath,0));
  53. }
  54. }
  55. public Optional<byte[]> getAndInitialize(String classname, byte[] bytes,
  56. ClassLoader loader, ProtectionDomain protectionDomain) {
  57. if (!enabled) {
  58. return null;
  59. }
  60. byte[] res = get(classname, bytes);
  61. if (Arrays.equals(SAME_BYTES, res)) {
  62. return Optional.empty();
  63. } else if (res != null) {
  64. initializeClass(classname, res, loader, protectionDomain);
  65. return Optional.of(res);
  66. }
  67. return null;
  68. }
  69. private byte[] get(String classname, byte bytes[]) {
  70. String key = generateKey(classname, bytes);
  71. byte[] res = cacheMap.get(key);
  72. return res;
  73. }
  74. public void put(String classname, byte[] origbytes, byte[] wovenbytes) {
  75. if (!enabled) {
  76. return;
  77. }
  78. String key = generateKey(classname, origbytes);
  79. if (wovenbytes == null || Arrays.equals(origbytes, wovenbytes)) {
  80. cacheMap.put(key, SAME_BYTES);
  81. return;
  82. }
  83. cacheMap.put(key, wovenbytes);
  84. }
  85. private String generateKey(String classname, byte[] bytes) {
  86. CRC32 checksum = new CRC32();
  87. checksum.update(bytes);
  88. long crc = checksum.getValue();
  89. classname = classname.replace("/", ".");
  90. return new String(classname + "-" + crc);
  91. }
  92. private static class StoreableCachingMap extends HashMap {
  93. private String folder;
  94. private static final String CACHENAMEIDX = "cache.idx";
  95. private long lastStored = System.currentTimeMillis();
  96. private static int DEF_STORING_TIMER = 60000; //ms
  97. private int storingTimer;
  98. private transient Trace trace;
  99. private void initTrace(){
  100. trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class);
  101. }
  102. // private StoreableCachingMap(String folder) {
  103. // this.folder = folder;
  104. // initTrace();
  105. // }
  106. private StoreableCachingMap(String folder, int storingTimer){
  107. this.folder = folder;
  108. initTrace();
  109. this.storingTimer = storingTimer;
  110. }
  111. public static StoreableCachingMap init(String folder) {
  112. return init(folder,DEF_STORING_TIMER);
  113. }
  114. public static StoreableCachingMap init(String folder, int storingTimer) {
  115. File file = new File(folder + File.separator + CACHENAMEIDX);
  116. if (file.exists()) {
  117. try {
  118. ObjectInputStream in = new ObjectInputStream(
  119. new FileInputStream(file));
  120. // Deserialize the object
  121. StoreableCachingMap sm = (StoreableCachingMap) in.readObject();
  122. sm.initTrace();
  123. in.close();
  124. return sm;
  125. } catch (Exception e) {
  126. Trace trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class);
  127. trace.error("Error reading Storable Cache", e);
  128. }
  129. }
  130. return new StoreableCachingMap(folder,storingTimer);
  131. }
  132. @Override
  133. public Object get(Object obj) {
  134. try {
  135. if (super.containsKey(obj)) {
  136. String path = (String) super.get(obj);
  137. if (path.equals(SAME_BYTES_STRING)) {
  138. return SAME_BYTES;
  139. }
  140. return readFromPath(path);
  141. } else {
  142. return null;
  143. }
  144. } catch (IOException e) {
  145. trace.error("Error reading key:"+obj.toString(),e);
  146. Dump.dumpWithException(e);
  147. }
  148. return null;
  149. }
  150. @Override
  151. public Object put(Object key, Object value) {
  152. try {
  153. String path = null;
  154. byte[] valueBytes = (byte[]) value;
  155. if (Arrays.equals(valueBytes, SAME_BYTES)) {
  156. path = SAME_BYTES_STRING;
  157. } else {
  158. path = writeToPath((String) key, valueBytes);
  159. }
  160. Object result = super.put(key, path);
  161. storeMap();
  162. return result;
  163. } catch (IOException e) {
  164. trace.error("Error inserting in cache: key:"+key.toString() + "; value:"+value.toString(), e);
  165. Dump.dumpWithException(e);
  166. }
  167. return null;
  168. }
  169. public void storeMap() {
  170. long now = System.currentTimeMillis();
  171. if ((now - lastStored ) < storingTimer){
  172. return;
  173. }
  174. File file = new File(folder + File.separator + CACHENAMEIDX);;
  175. try {
  176. ObjectOutputStream out = new ObjectOutputStream(
  177. new FileOutputStream(file));
  178. // Deserialize the object
  179. out.writeObject(this);
  180. out.close();
  181. lastStored = now;
  182. } catch (Exception e) {
  183. trace.error("Error storing cache; cache file:"+file.getAbsolutePath(), e);
  184. Dump.dumpWithException(e);
  185. }
  186. }
  187. private byte[] readFromPath(String fullPath) throws IOException {
  188. FileInputStream is = null ;
  189. try{
  190. is = new FileInputStream(fullPath);
  191. }
  192. catch (FileNotFoundException e){
  193. //may be caused by a generated class that has been stored in generated cache but not saved at cache folder
  194. System.out.println("FileNotFoundExceptions: The aspectj cache is corrupt. Please clean it and reboot the server. Cache path:"+this.folder );
  195. e.printStackTrace();
  196. return null;
  197. }
  198. ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  199. int nRead;
  200. byte[] data = new byte[16384];
  201. while ((nRead = is.read(data, 0, data.length)) != -1) {
  202. buffer.write(data, 0, nRead);
  203. }
  204. buffer.flush();
  205. is.close();
  206. return buffer.toByteArray();
  207. }
  208. private String writeToPath(String key, byte[] bytes) throws IOException {
  209. String fullPath = folder + File.separator + key;
  210. FileOutputStream fos = new FileOutputStream(fullPath);
  211. fos.write(bytes);
  212. fos.flush();
  213. fos.close();
  214. return fullPath;
  215. }
  216. }
  217. private void initializeClass(String className, byte[] bytes,
  218. ClassLoader loader, ProtectionDomain protectionDomain) {
  219. String[] generatedClassesNames = getGeneratedClassesNames(className,bytes);
  220. if (generatedClassesNames == null) {
  221. return;
  222. }
  223. for (String generatedClassName : generatedClassesNames) {
  224. byte[] generatedBytes = get(generatedClassName, bytes);
  225. if (protectionDomain == null) {
  226. defineClass(loader, generatedClassName, generatedBytes);
  227. } else {
  228. defineClass(loader, generatedClassName, generatedBytes,
  229. protectionDomain);
  230. }
  231. }
  232. }
  233. private String[] getGeneratedClassesNames(String className, byte[] bytes) {
  234. String key = generateKey(className, bytes);
  235. byte[] readBytes = generatedCache.get(key);
  236. if (readBytes == null) {
  237. return null;
  238. }
  239. String readString = new String(readBytes);
  240. return readString.split(GENERATED_CACHE_SEPARATOR);
  241. }
  242. public void addGeneratedClassesNames(String parentClassName, byte[] parentBytes, String generatedClassName) {
  243. if (!enabled) {
  244. return;
  245. }
  246. String key = generateKey(parentClassName, parentBytes);
  247. byte[] storedBytes = generatedCache.get(key);
  248. if (storedBytes == null) {
  249. generatedCache.put(key, generatedClassName.getBytes());
  250. } else {
  251. String storedClasses = new String(storedBytes);
  252. storedClasses += GENERATED_CACHE_SEPARATOR + generatedClassName;
  253. generatedCache.put(key, storedClasses.getBytes());
  254. }
  255. }
  256. private Method defineClassMethod = null;
  257. private Method defineClassWithProtectionDomainMethod = null;
  258. private void defineClass(ClassLoader loader, String name, byte[] bytes) {
  259. Object clazz = null;
  260. try {
  261. if (defineClassMethod == null) {
  262. defineClassMethod = ClassLoader.class.getDeclaredMethod(
  263. "defineClass", new Class[] { String.class,
  264. bytes.getClass(), int.class, int.class });
  265. }
  266. defineClassMethod.setAccessible(true);
  267. clazz = defineClassMethod.invoke(loader, new Object[] { name,
  268. bytes, 0, bytes.length});
  269. } catch (InvocationTargetException e) {
  270. if (e.getTargetException() instanceof LinkageError) {
  271. e.printStackTrace();
  272. } else {
  273. System.out.println("define generated class failed"
  274. + e.getTargetException());
  275. }
  276. } catch (Exception e) {
  277. e.printStackTrace();
  278. Dump.dumpWithException(e);
  279. }
  280. }
  281. private void defineClass(ClassLoader loader, String name, byte[] bytes,
  282. ProtectionDomain protectionDomain) {
  283. Object clazz = null;
  284. try {
  285. // System.out.println(">> Defining with protection domain " + name +
  286. // " pd=" + protectionDomain);
  287. if (defineClassWithProtectionDomainMethod == null) {
  288. defineClassWithProtectionDomainMethod = ClassLoader.class
  289. .getDeclaredMethod("defineClass", new Class[] {
  290. String.class, bytes.getClass(), int.class,
  291. int.class, ProtectionDomain.class });
  292. }
  293. defineClassWithProtectionDomainMethod.setAccessible(true);
  294. clazz = defineClassWithProtectionDomainMethod.invoke(loader,
  295. new Object[] { name, bytes, 0,
  296. bytes.length, protectionDomain });
  297. } catch (InvocationTargetException e) {
  298. if (e.getTargetException() instanceof LinkageError) {
  299. e.printStackTrace();
  300. // is already defined (happens for X$ajcMightHaveAspect
  301. // interfaces since aspects are reweaved)
  302. // TODO maw I don't think this is OK and
  303. } else {
  304. e.printStackTrace();
  305. }
  306. }catch (NullPointerException e) {
  307. System.out.println("NullPointerException loading class:"+name+". Probabily caused by a corruput cache. Please clean it and reboot the server");
  308. } catch (Exception e) {
  309. e.printStackTrace();
  310. Dump.dumpWithException(e);
  311. }
  312. }
  313. }