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

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
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. }