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