1 package org.bsf.smartValueObject.tools; 2 3 import javassist.*; 4 import org.apache.commons.logging.Log; 5 import org.apache.commons.logging.LogFactory; 6 7 import java.io.*; 8 import java.util.Properties ; 9 10 18 public class JavaAssistInstrumentor implements Instrumentor { 19 private static final Log log = LogFactory.getLog(JavaAssistInstrumentor.class); 20 21 private static final ClassPool pool; 22 23 private static final CodeConverter converter = new CodeConverter(); 24 25 private static InstClassLoader instCL = new InstClassLoader(JavaAssistInstrumentor.class.getClassLoader()); 26 27 private CtClass ctclass = null; 28 29 private CtMethod standardTrap = null; 30 31 static { 32 pool = ClassPool.getDefault(); 34 pool.insertClassPath(new LoaderClassPath(JavaAssistInstrumentor.class.getClassLoader())); 35 } 36 37 41 public JavaAssistInstrumentor() { 42 } 43 44 50 public JavaAssistInstrumentor(String name) throws InstrumentorException { 51 modifyClass(name); 52 } 53 54 59 public JavaAssistInstrumentor(Class clazz) throws InstrumentorException { 60 this(clazz.getName()); 61 } 62 63 public void modifyClass(String name) throws InstrumentorException { 64 if (name.endsWith(".class")) { 65 name = fileToClass(name); 66 } 67 68 try { 69 ctclass = pool.get(name); 70 if (alreadyModified(ctclass)) { 71 return; 72 } else { 73 modifyClass(ctclass); 74 } 75 } catch (Exception e) { 76 throw new InstrumentorException("JavaAssistInstrumentor: error while transforming", e); 77 } 78 } 79 80 public void modifyClass(String basedir, String file) throws InstrumentorException { 81 try { 82 FileInputStream fis = new FileInputStream(new File(basedir, file)); 83 byte[] bytecode = readStream(fis); 84 pool.insertClassPath(new ByteArrayClassPath(fileToClass(file), bytecode)); 85 pool.appendClassPath(basedir); 86 } catch (NotFoundException e) { 87 throw new InstrumentorException(e); 88 } catch (IOException e) { 89 throw new InstrumentorException(e); 90 } 91 modifyClass(file); 92 } 93 94 public byte[] getBytecode() throws InstrumentorException { 95 if (ctclass == null) throw new IllegalStateException ("use modifyClass first"); 96 try { 97 return ctclass.toBytecode(); 98 } catch (Exception e) { 99 throw new InstrumentorException(e); 100 } 101 } 102 103 public Class defineClass() { 104 return instCL.loadClass(ctclass); 105 } 106 107 112 private CtClass modifyClass(CtClass cc) throws InstrumentorException { 113 log.debug("modifyClass: " + cc); 114 115 try { 116 standardTrap = createTrapWrite(cc); 117 addFieldInterceptors(cc); 118 makeFieldsPublic(cc); 119 makeVersionable(cc); 120 cc.instrument(converter); 121 } catch (Exception e) { 122 log.warn("exeception while modifying", e); 123 throw new InstrumentorException(e); 124 } 125 126 return cc; 127 } 128 129 133 private void makeFieldsPublic(CtClass cc) { 134 CtField[] fields = cc.getDeclaredFields(); 135 for (int i = 0; i < fields.length; i++) { 136 CtField field = fields[i]; 137 if ((field.getModifiers() & Modifier.STATIC) == 0) 138 field.setModifiers(Modifier.PUBLIC); 139 } 140 } 141 142 150 private void makeVersionable(CtClass cc) 151 throws CannotCompileException, NotFoundException, InstrumentorException { 152 CtField versionField = addVersionField(cc); 153 CtClass versionInterface = pool.get(VERSIONINTERFACE); 154 addDelegations(versionInterface, versionField, cc); 155 cc.addInterface(versionInterface); 156 } 157 158 167 private void addDelegations(CtClass iface, CtField field, CtClass declaring) 168 throws InstrumentorException, NotFoundException, CannotCompileException { 169 170 if (!iface.isInterface()) { 171 throw new InstrumentorException("need Interface"); 172 } 173 if (!field.getType().subtypeOf(iface)) { 174 throw new InstrumentorException("field doesn't implement interface"); 175 } 176 177 CtMethod[] methods = iface.getDeclaredMethods(); 179 for (int i = 0; i < methods.length; i++) { 180 CtMethod method = methods[i]; 181 StringBuffer body = new StringBuffer (); 182 if (method.getReturnType() != CtClass.voidType) { 183 body.append("return "); 184 } 185 186 body.append(field.getName() + (".") + method.getName() + "($$);"); 189 190 CtMethod newMethod = CtNewMethod.make( 193 method.getReturnType(), 194 method.getName(), 195 method.getParameterTypes(), 196 method.getExceptionTypes(), 197 body.toString(), 198 declaring); 199 declaring.addMethod(newMethod); 200 } 201 } 202 203 204 private void addFieldInterceptors(CtClass cc) 205 throws NotFoundException, CannotCompileException { 206 CtField[] fields = cc.getDeclaredFields(); 207 208 for (int i = 0; i < fields.length; i++) { 209 if ((fields[i].getModifiers() & Modifier.STATIC) != 0) { 210 continue; 211 } 212 213 addFieldInterceptor(fields[i], SMARTCONTAINERS); 214 } 215 } 216 217 230 private void addFieldInterceptor(CtField field, Properties ifaces) 231 throws NotFoundException, CannotCompileException { 232 String name = field.getName(); 233 CtClass cc = field.getDeclaringClass(); 234 log.debug("addFieldInterceptor: " + name); 235 236 CtMethod trap; 237 String fieldtype = field.getType().getName(); 238 String replacement = ifaces.getProperty(fieldtype); 239 240 if (replacement != null) { 243 trap = createTrapWriteGeneric(cc, fieldtype, replacement); 244 } else { 245 trap = standardTrap; 247 } 248 249 CtClass[] writeParam = new CtClass[2]; 250 writeParam[0] = pool.get("java.lang.Object"); 251 writeParam[1] = field.getType(); 252 CtMethod method = CtNewMethod.wrapped( 253 CtClass.voidType, fieldWrite(name), writeParam, null, trap, CtMethod.ConstParameter.string(name), cc); method.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 263 cc.addMethod(method); 264 265 converter.replaceFieldWrite(field, cc, fieldWrite(name)); 266 } 267 268 269 private static String fieldWrite(String name) { 270 return "write_" + name; 271 } 272 273 282 private CtField addVersionField(CtClass cc) 283 throws CannotCompileException, NotFoundException { 284 log.debug("addVersionField: " + cc); 285 CtField field; 286 287 field = createVersionField(cc); 288 cc.addField(field, CtField.Initializer.byNew(pool.get(VERSIONCLASS))); 291 292 return field; 293 } 294 295 300 private CtField createVersionField(CtClass declaring) 301 throws CannotCompileException, NotFoundException { 302 String name = VERSIONFIELD; 303 CtClass type = pool.get(VERSIONCLASS); 304 CtField field = new CtField(type, name, declaring); 305 field.setModifiers(Modifier.PUBLIC); 306 307 return field; 308 } 309 310 323 private CtMethod createTrapWrite(CtClass cc) 324 throws CannotCompileException { 325 String classname = cc.getName(); 326 327 String body = 328 "protected static Object trapWrite(Object[] args, String name) {" + 329 classname + " foo = (" + classname + ") args[0];" + 330 "try {" + 331 " java.lang.reflect.Field field = foo.getClass().getField(name);" + 332 " if (" + VERSIONHELPER + ".doEquals(field)) {" + 333 " if (field.get(foo) != null && field.get(foo).equals(args[1]))" + 334 " return null;" + 335 " else if (args[1] == null)" + 336 " return null;" + 337 " }" + 338 " field.set(foo, args[1]); " + 339 " ((" + VERSIONINTERFACE + ")foo)." + VERSIONMETHOD + "(field.getName());" + 340 "} catch (Throwable t) { throw new RuntimeException(t); } " + 341 "return null;" + 342 "}"; 343 344 log.debug(body); 345 return CtNewMethod.make(body, cc); 346 } 347 348 361 private CtMethod createTrapWriteGeneric(CtClass cc, String dumb, String smart) 362 throws CannotCompileException { 363 String classname = cc.getName(); 364 int lastDot = dumb.lastIndexOf('.'); 365 String suffix; 366 if (lastDot != -1) { 367 suffix = dumb.substring(lastDot + 1); 368 } else { 369 suffix = dumb; 370 } 371 372 String body = 373 "protected static Object trapWrite" + suffix + "(Object[] args, String name) {" + 374 classname + " foo = (" + classname + ") args[0];" + 375 "try { " + 376 " java.lang.reflect.Field field = foo.getClass().getField(name);" + 377 dumb + " o = new " + smart + 378 " ((" + dumb + ") args[1]," + 379 " new " + VERSIONCLASS + "());" + 383 " field.set(foo, o); " + 384 "} catch (Throwable t) {} " + 385 "return null;" + 386 "}"; 387 388 log.debug(body); 389 return CtNewMethod.make(body, cc); 390 } 391 392 397 private static class InstClassLoader extends ClassLoader { 398 402 public InstClassLoader(ClassLoader c) { 403 super(c); 404 } 405 406 public Class loadClass(CtClass cc) throws ClassFormatError { 407 byte[] bytecode; 408 try { 409 bytecode = cc.toBytecode(); 410 } catch (Exception e) { 411 throw new ClassFormatError (e.getMessage()); 412 } 413 414 return loadClass(cc.getName(), bytecode); 415 } 416 417 public Class loadClass(String name, byte[] bytecode) throws ClassFormatError { 418 Class c = defineClass(name, bytecode, 0, bytecode.length); 419 resolveClass(c); 420 return c; 421 } 422 423 public Class loadAndDefine(String name) throws 424 ClassNotFoundException { 425 String cname = name.replace('.', '/') + ".class"; 426 InputStream ins = getResourceAsStream(cname); 427 if (ins == null) { 428 throw new ClassNotFoundException (); 429 } 430 431 byte[] bytecode; 432 try { 433 bytecode = readStream(ins); 434 } catch (IOException e) { 435 throw new ClassFormatError (e.getMessage()); 436 } 437 438 return loadClass(name, bytecode); 439 } 440 441 protected Class findClass(String name) throws ClassNotFoundException { 442 throw new ClassNotFoundException (); 443 } 444 } 445 446 447 private static byte[] readStream(InputStream fin) throws IOException { 448 byte[][] bufs = new byte[8][]; 449 int bufsize = 4096; 450 451 for (int i = 0; i < 8; ++i) { 452 bufs[i] = new byte[bufsize]; 453 int size = 0; 454 int len; 455 do { 456 len = fin.read(bufs[i], size, bufsize - size); 457 if (len >= 0) 458 size += len; 459 else { 460 byte[] result = new byte[bufsize - 4096 + size]; 461 int s = 0; 462 for (int j = 0; j < i; ++j) { 463 System.arraycopy(bufs[j], 0, result, s, s + 4096); 464 s = s + s + 4096; 465 } 466 467 System.arraycopy(bufs[i], 0, result, s, size); 468 return result; 469 } 470 } while (size < bufsize); 471 bufsize *= 2; 472 } 473 474 throw new IOException("too much data"); 475 } 476 477 private static String fileToClass(String filename) { 478 filename = filename. 479 replace(File.separatorChar, '.'). 480 substring(0, filename.length() - 6); 481 return filename; 482 } 483 484 485 private static boolean alreadyModified(CtClass ctclass) throws NotFoundException { 486 CtClass[] ifaces = ctclass.getInterfaces(); 487 for (int i = 0; i < ifaces.length; i++) { 488 CtClass iface = ifaces[i]; 489 if (iface.getName().equals(VERSIONINTERFACE)) 490 return true; 491 } 492 return false; 493 } 494 } 495 | Popular Tags |