001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.amber.gen;
031:
032: import com.caucho.amber.field.AmberField;
033: import com.caucho.amber.manager.AmberContainer;
034: import com.caucho.amber.type.*;
035: import com.caucho.bytecode.*;
036: import com.caucho.config.ConfigException;
037: import com.caucho.java.JavaCompiler;
038: import com.caucho.java.WorkDir;
039: import com.caucho.java.gen.DependencyComponent;
040: import com.caucho.java.gen.GenClass;
041: import com.caucho.java.gen.JavaClassGenerator;
042: import com.caucho.loader.*;
043: import com.caucho.loader.enhancer.ClassEnhancer;
044: import com.caucho.loader.enhancer.EnhancerPrepare;
045: import com.caucho.log.Log;
046: import com.caucho.util.L10N;
047: import com.caucho.vfs.Path;
048: import com.caucho.vfs.Vfs;
049:
050: import java.io.IOException;
051: import java.lang.reflect.Method;
052: import java.util.ArrayList;
053: import java.util.logging.Level;
054: import java.util.logging.Logger;
055:
056: /**
057: * Enhancing the java objects for Amber mapping.
058: */
059: public class AmberEnhancer implements AmberGenerator, ClassEnhancer {
060: private static final L10N L = new L10N(AmberEnhancer.class);
061: private static final Logger log = Log.open(AmberEnhancer.class);
062:
063: private Path _configDirectory;
064: private boolean _useHibernateFiles;
065:
066: private AmberContainer _amberContainer;
067:
068: private EnhancerPrepare _prepare;
069: private Path _workDir;
070: private Path _postWorkDir;
071:
072: private ArrayList<String> _pendingClassNames = new ArrayList<String>();
073:
074: public AmberEnhancer(AmberContainer amberContainer) {
075: _amberContainer = amberContainer;
076: _workDir = WorkDir.getLocalWorkDir().lookup("pre-enhance");
077: _postWorkDir = WorkDir.getLocalWorkDir().lookup("post-enhance");
078:
079: _prepare = new EnhancerPrepare();
080: _prepare.setClassLoader(Thread.currentThread()
081: .getContextClassLoader());
082: _prepare.setWorkPath(WorkDir.getLocalWorkDir());
083: _prepare.addEnhancer(this );
084: }
085:
086: /**
087: * Sets the config directory.
088: */
089: public void setConfigDirectory(Path dir) {
090: _configDirectory = dir;
091: }
092:
093: /**
094: * Returns the work directory.
095: */
096: public Path getWorkDir() {
097: return _workDir;
098: }
099:
100: /**
101: * Returns the work directory.
102: */
103: public Path getPostWorkDir() {
104: return _postWorkDir;
105: }
106:
107: /**
108: * Initialize the enhancer.
109: */
110: public void init() throws Exception {
111: }
112:
113: /**
114: * Checks to see if the preloaded class is modified.
115: */
116: protected boolean isModified(Class preloadedClass) {
117: try {
118: Method init = preloadedClass.getMethod("_caucho_init",
119: new Class[] { Path.class });
120:
121: if (_configDirectory != null)
122: init.invoke(null, new Object[] { _configDirectory });
123: else
124: init.invoke(null, new Object[] { Vfs.lookup() });
125:
126: Method isModified = preloadedClass.getMethod(
127: "_caucho_is_modified", new Class[0]);
128:
129: Object value = isModified.invoke(null, new Object[0]);
130:
131: if (Boolean.FALSE.equals(value)) {
132: loadEntityType(preloadedClass, preloadedClass
133: .getClassLoader());
134: return false;
135: } else
136: return true;
137: } catch (Throwable e) {
138: log.log(Level.FINER, e.toString(), e);
139:
140: return true;
141: }
142: }
143:
144: /**
145: * Returns true if the class should be enhanced.
146: */
147: public boolean shouldEnhance(String className) {
148: className = className.replace('/', '.');
149:
150: int p = className.lastIndexOf('-');
151:
152: if (p > 0)
153: className = className.substring(0, p);
154:
155: p = className.lastIndexOf('$');
156:
157: if (p > 0)
158: className = className.substring(0, p);
159:
160: AbstractEnhancedType type;
161:
162: type = _amberContainer.getEntity(className);
163:
164: if (type != null && type.isEnhanced())
165: return true;
166:
167: type = _amberContainer.getMappedSuperclass(className);
168:
169: if (type != null && type.isEnhanced())
170: return true;
171:
172: type = _amberContainer.getEmbeddable(className);
173:
174: if (type != null && type.isEnhanced())
175: return true;
176:
177: type = _amberContainer.getListener(className);
178:
179: if (type != null && type.isEnhanced())
180: return true;
181:
182: return false;
183:
184: /*
185: Thread thread = Thread.currentThread();
186: ClassLoader oldLoader = thread.getContextClassLoader();
187: try {
188: thread.setContextClassLoader(getRawLoader());
189:
190: Class baseClass = Class.forName(className, false, getRawLoader());
191:
192: type = loadEntityType(baseClass, getRawLoader());
193: } catch (ClassNotFoundException e) {
194: return false;
195: } finally {
196: thread.setContextClassLoader(oldLoader);
197: }
198:
199: if (type == null)
200: return false;
201:
202: return className.equals(type.getName()) || type.isFieldAccess();
203: */
204: }
205:
206: /**
207: * Returns true if the class should be enhanced.
208: */
209: private EntityType loadEntityType(Class cl, ClassLoader loader) {
210: EntityType parentType = null;
211:
212: for (; cl != null; cl = cl.getSuperclass()) {
213: java.net.URL url;
214:
215: String className = cl.getName();
216:
217: EntityType type = _amberContainer.getEntity(className);
218:
219: if (parentType == null)
220: parentType = type;
221:
222: if (type != null && !type.startConfigure())
223: return type;
224:
225: type = loadEntityTypeImpl(cl, loader);
226:
227: if (type != null && !type.startConfigure())
228: return type;
229: }
230:
231: return parentType;
232: }
233:
234: protected EntityType loadEntityTypeImpl(Class cl,
235: ClassLoader rawLoader) {
236: return null;
237: }
238:
239: /**
240: * Enhances the class.
241: */
242: public void preEnhance(JavaClass baseClass) throws Exception {
243: RelatedType type = _amberContainer.getEntity(baseClass
244: .getName());
245:
246: if (type == null)
247: type = _amberContainer.getMappedSuperclass(baseClass
248: .getName());
249:
250: if (type instanceof SubEntityType) {
251: SubEntityType subType = (SubEntityType) type;
252:
253: String parentClass = subType.getParentType()
254: .getInstanceClassName();
255: baseClass.setSuperClass(parentClass.replace('.', '/'));
256: }
257: }
258:
259: /**
260: * Enhances the class.
261: */
262: public void enhance(GenClass genClass, JClass baseClass,
263: String extClassName) throws Exception {
264: String className = baseClass.getName();
265:
266: RelatedType type = _amberContainer.getEntity(className);
267:
268: if (type == null)
269: type = _amberContainer.getMappedSuperclass(className);
270:
271: // Type can be null for subclasses and inner classes that need fixups
272: if (type != null) {
273: // type is EntityType or MappedSuperclassType
274:
275: log.info("Amber enhancing class " + className);
276:
277: // XXX: _amberContainerenceUnitenceUnit.configure();
278:
279: type.init();
280:
281: genClass.addInterfaceName(type.getComponentInterfaceName());
282:
283: genClass.addImport("java.util.logging.*");
284: genClass.addImport("com.caucho.amber.manager.*");
285: genClass.addImport("com.caucho.amber.entity.*");
286: genClass.addImport("com.caucho.amber.type.*");
287:
288: AmberMappedComponent componentGenerator = type
289: .getComponentGenerator();
290:
291: componentGenerator.setRelatedType((RelatedType) type);
292: componentGenerator.setBaseClassName(baseClass.getName());
293: componentGenerator.setExtClassName(extClassName);
294:
295: genClass.addComponent(componentGenerator);
296:
297: DependencyComponent dependency = genClass
298: .addDependencyComponent();
299: dependency.addDependencyList(type.getDependencies());
300:
301: return;
302:
303: //_amberContainerenceUnitenceUnit.generate();
304: // generate(type);
305:
306: // compile();
307:
308: // XXX: _amberContainerenceUnitenceUnit.initEntityHomes();
309: }
310:
311: ListenerType listenerType = _amberContainer
312: .getListener(className);
313:
314: // Type can be null for subclasses and inner classes that need fixups
315: if (listenerType != null) {
316: if (log.isLoggable(Level.INFO))
317: log.log(Level.INFO, "Amber enhancing class "
318: + className);
319:
320: listenerType.init();
321:
322: genClass
323: .addInterfaceName("com.caucho.amber.entity.Listener");
324:
325: ListenerComponent listener = new ListenerComponent();
326:
327: listener.setListenerType(listenerType);
328: listener.setBaseClassName(baseClass.getName());
329: listener.setExtClassName(extClassName);
330:
331: genClass.addComponent(listener);
332: }
333:
334: EmbeddableType embeddableType = _amberContainer
335: .getEmbeddable(className);
336:
337: // Type can be null for subclasses and inner classes that need fixups
338: if (embeddableType != null) {
339: if (log.isLoggable(Level.INFO))
340: log.log(Level.INFO, "Amber enhancing class "
341: + className);
342:
343: embeddableType.init();
344:
345: genClass
346: .addInterfaceName("com.caucho.amber.entity.Embeddable");
347:
348: EmbeddableComponent embeddable = new EmbeddableComponent();
349:
350: embeddable.setEmbeddableType(embeddableType);
351: embeddable.setBaseClassName(baseClass.getName());
352: embeddable.setExtClassName(extClassName);
353:
354: genClass.addComponent(embeddable);
355: }
356: }
357:
358: /**
359: * Generates the type.
360: */
361: public void generate(AbstractEnhancedType type) throws Exception {
362: String className = type.getBeanClass().getName();
363:
364: if (!isModified(className))
365: return;
366:
367: JavaClassGenerator javaGen = new JavaClassGenerator();
368:
369: javaGen.setWorkDir(getWorkDir());
370:
371: String extClassName = type.getBeanClass().getName()
372: + "__ResinExt";
373: type.setInstanceClassName(extClassName);
374: type.setEnhanced(true);
375:
376: _pendingClassNames.add(type.getInstanceClassName());
377:
378: generateJava(javaGen, type);
379: }
380:
381: /**
382: * Generates the type.
383: */
384: public void generateJava(JavaClassGenerator javaGen,
385: AbstractEnhancedType type) throws Exception {
386: if (type.isGenerated())
387: return;
388:
389: type.setGenerated(true);
390:
391: _prepare.renameClass(type.getBeanClass().getName(), type
392: .getBeanClass().getName());
393:
394: GenClass javaClass = new GenClass(type.getInstanceClassName());
395:
396: javaClass.setSuperClassName(type.getBeanClass().getName());
397:
398: javaClass.addImport("java.util.logging.*");
399: javaClass.addImport("com.caucho.amber.manager.*");
400: javaClass.addImport("com.caucho.amber.entity.*");
401: javaClass.addImport("com.caucho.amber.type.*");
402:
403: AmberMappedComponent componentGenerator = type
404: .getComponentGenerator();
405:
406: if (componentGenerator != null) {
407: // type is EntityType or MappedSuperclassType
408:
409: javaClass
410: .addInterfaceName(type.getComponentInterfaceName());
411:
412: type.setEnhanced(true);
413:
414: componentGenerator.setRelatedType((RelatedType) type);
415: componentGenerator.setBaseClassName(type.getBeanClass()
416: .getName());
417:
418: //String extClassName = gen.getBaseClassName() + "__ResinExt";
419: // type.setInstanceClassName(extClassName);
420:
421: componentGenerator.setExtClassName(type
422: .getInstanceClassName());
423:
424: javaClass.addComponent(componentGenerator);
425: } else if (type instanceof ListenerType) {
426: javaClass
427: .addInterfaceName("com.caucho.amber.entity.Listener");
428:
429: type.setEnhanced(true);
430:
431: ListenerComponent listener = new ListenerComponent();
432:
433: listener.setListenerType((ListenerType) type);
434: listener.setBaseClassName(type.getBeanClass().getName());
435:
436: listener.setExtClassName(type.getInstanceClassName());
437:
438: javaClass.addComponent(listener);
439: } else {
440: javaClass
441: .addInterfaceName("com.caucho.amber.entity.Embeddable");
442:
443: type.setEnhanced(true);
444:
445: EmbeddableComponent embeddable = new EmbeddableComponent();
446:
447: embeddable.setEmbeddableType((EmbeddableType) type);
448: embeddable.setBaseClassName(type.getBeanClass().getName());
449:
450: embeddable.setExtClassName(type.getInstanceClassName());
451:
452: javaClass.addComponent(embeddable);
453: }
454:
455: javaGen.generate(javaClass);
456:
457: // _pendingClassNames.add(extClassName);
458: }
459:
460: private boolean isModified(String className) {
461: try {
462: ClassLoader loader = _amberContainer.getParentClassLoader();
463: ClassLoader tempLoader = ((DynamicClassLoader) loader)
464: .getNewTempClassLoader();
465: DynamicClassLoader workLoader = SimpleLoader.create(
466: tempLoader, getPostWorkDir());
467: workLoader.setServletHack(true);
468:
469: Class cl = Class.forName(className.replace('/', '.'),
470: false, workLoader);
471:
472: Method init = cl.getMethod("_caucho_init",
473: new Class[] { Path.class });
474: Method modified = cl.getMethod("_caucho_is_modified",
475: new Class[0]);
476:
477: init.invoke(null, Vfs.lookup());
478:
479: return (Boolean) modified.invoke(null);
480: } catch (Exception e) {
481: log.log(Level.FINEST, e.toString(), e);
482: } catch (Throwable e) {
483: log.log(Level.FINER, e.toString(), e);
484: }
485:
486: return true;
487: }
488:
489: /**
490: * Compiles the pending classes.
491: */
492: public void compile() throws Exception {
493: if (_pendingClassNames.size() == 0)
494: return;
495:
496: ArrayList<String> classNames = new ArrayList<String>(
497: _pendingClassNames);
498: _pendingClassNames.clear();
499:
500: String[] javaFiles = new String[classNames.size()];
501:
502: for (int i = 0; i < classNames.size(); i++) {
503: String className = classNames.get(i);
504:
505: String javaName = className.replace('.', '/') + ".java";
506:
507: javaFiles[i] = javaName;
508: }
509:
510: EntityGenerator gen = new EntityGenerator();
511: gen.setSearchPath(_configDirectory);
512: // XXX:
513: // gen.setClassDir(getPath());
514:
515: JavaCompiler compiler = gen.getCompiler();
516:
517: compiler.setClassDir(getWorkDir());
518: compiler.compileBatch(javaFiles);
519:
520: for (int i = 0; i < classNames.size(); i++) {
521: String extClassName = classNames.get(i);
522: int tail = extClassName.length() - "__ResinExt".length();
523:
524: String baseClassName = extClassName.substring(0, tail);
525:
526: // fixup(baseClassName, extClassName);
527: }
528: }
529:
530: /**
531: * Enhances the class.
532: */
533: public void postEnhance(JavaClass baseClass) throws Exception {
534: String className = baseClass.getThisClass();
535:
536: ArrayList<FieldMap> fieldMaps = new ArrayList<FieldMap>();
537:
538: JClass this Class = _amberContainer.getJClassLoader().forName(
539: className.replace('/', '.'));
540:
541: if (this Class == null)
542: return;
543:
544: // Cache entity JClass for next fixup.
545: JClass entityClass = this Class;
546:
547: // Field-based fixup.
548: do {
549: AbstractStatefulType type;
550:
551: type = _amberContainer.getEntity(this Class.getName());
552:
553: if (type == null)
554: type = _amberContainer.getMappedSuperclass(this Class
555: .getName());
556:
557: if (type == null)
558: type = _amberContainer.getEmbeddable(this Class
559: .getName());
560:
561: if (type == null || !type.isFieldAccess())
562: continue;
563:
564: if (type instanceof EmbeddableType)
565: continue;
566:
567: if (type instanceof EntityType) {
568: EntityType entityType = (EntityType) type;
569:
570: for (AmberField field : entityType.getId().getKeys()) {
571: fieldMaps.add(new FieldMap(baseClass, field
572: .getName()));
573: }
574: }
575:
576: for (AmberField field : type.getFields()) {
577: fieldMaps.add(new FieldMap(baseClass, field.getName()));
578: }
579: } while ((this Class = this Class.getSuperClass()) != null);
580:
581: if (fieldMaps.size() > 0) {
582: FieldFixupAnalyzer analyzer = new FieldFixupAnalyzer(
583: fieldMaps);
584:
585: for (JavaMethod javaMethod : baseClass.getMethodList()) {
586: CodeVisitor visitor = new CodeVisitor(baseClass,
587: javaMethod.getCode());
588:
589: visitor.analyze(analyzer, true);
590: }
591: }
592: }
593:
594: /**
595: * Parses the configuration file.
596: */
597: public void configure(AbstractEnhancedType type)
598: throws ConfigException, IOException {
599: }
600:
601: static class FieldMap {
602: private int _fieldRef = -1;
603: private int _getterRef;
604: private int _setterRef;
605:
606: FieldMap(com.caucho.bytecode.JavaClass baseClass,
607: String fieldName) {
608: ConstantPool pool = baseClass.getConstantPool();
609:
610: FieldRefConstant fieldRef = pool.getFieldRef(fieldName);
611:
612: if (fieldRef == null)
613: return;
614:
615: _fieldRef = fieldRef.getIndex();
616:
617: MethodRefConstant methodRef;
618:
619: String getterName = "__caucho_get_" + fieldName;
620:
621: methodRef = pool.addMethodRef(baseClass.getThisClass(),
622: getterName, "()" + fieldRef.getType());
623:
624: _getterRef = methodRef.getIndex();
625:
626: String setterName = "__caucho_set_" + fieldName;
627:
628: methodRef = pool.addMethodRef(baseClass.getThisClass(),
629: setterName, "(" + fieldRef.getType() + ")V");
630:
631: _setterRef = methodRef.getIndex();
632: }
633:
634: int getFieldRef() {
635: return _fieldRef;
636: }
637:
638: int getGetterRef() {
639: return _getterRef;
640: }
641:
642: int getSetterRef() {
643: return _setterRef;
644: }
645: }
646:
647: static class FieldFixupAnalyzer extends Analyzer {
648: private ArrayList<FieldMap> _fieldMap;
649:
650: FieldFixupAnalyzer(ArrayList<FieldMap> fieldMap) {
651: _fieldMap = fieldMap;
652: }
653:
654: int getGetter(int fieldRef) {
655: for (int i = _fieldMap.size() - 1; i >= 0; i--) {
656: FieldMap fieldMap = _fieldMap.get(i);
657:
658: if (fieldMap.getFieldRef() == fieldRef)
659: return fieldMap.getGetterRef();
660: }
661:
662: return -1;
663: }
664:
665: public void analyze(CodeVisitor visitor) {
666: switch (visitor.getOpcode()) {
667: case CodeVisitor.GETFIELD:
668: int getter = getGetter(visitor.getShortArg());
669:
670: if (getter > 0) {
671: visitor.setByteArg(0, CodeVisitor.INVOKEVIRTUAL);
672: visitor.setShortArg(1, getter);
673: }
674: break;
675: case CodeVisitor.PUTFIELD:
676: int setter = getSetter(visitor.getShortArg());
677:
678: if (setter > 0) {
679: visitor.setByteArg(0, CodeVisitor.INVOKEVIRTUAL);
680: visitor.setShortArg(1, setter);
681: }
682: break;
683: }
684: }
685:
686: int getSetter(int fieldRef) {
687: for (int i = _fieldMap.size() - 1; i >= 0; i--) {
688: FieldMap fieldMap = _fieldMap.get(i);
689:
690: if (fieldMap.getFieldRef() == fieldRef)
691: return fieldMap.getSetterRef();
692: }
693:
694: return -1;
695: }
696: }
697: }
|