Source Code Cross Referenced for InterfaceInvocationHandler.java in  » Database-ORM » XORM » org » xorm » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » XORM » org.xorm 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:          $Header: /cvsroot/xorm/xorm/src/org/xorm/InterfaceInvocationHandler.java,v 1.78 2004/04/27 23:18:35 wbiggs Exp $
003:
004:          This file is part of XORM.
005:
006:          XORM is free software; you can redistribute it and/or modify
007:          it under the terms of the GNU General Public License as published by
008:          the Free Software Foundation; either version 2 of the License, or
009:          (at your option) any later version.
010:
011:          XORM is distributed in the hope that it will be useful,
012:          but WITHOUT ANY WARRANTY; without even the implied warranty of
013:          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014:          GNU General Public License for more details.
015:
016:          You should have received a copy of the GNU General Public License
017:          along with XORM; if not, write to the Free Software
018:          Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
019:         */
020:        package org.xorm;
021:
022:        import java.lang.reflect.Member;
023:        import java.lang.reflect.Method;
024:        import java.lang.reflect.Modifier;
025:        import java.util.Collection;
026:        import java.util.HashMap;
027:        import java.util.Iterator;
028:        import java.util.List;
029:        import java.util.logging.Logger;
030:        import java.util.logging.Level;
031:
032:        import org.xorm.datastore.Column;
033:        import org.xorm.datastore.DataFetchGroup;
034:        import org.xorm.datastore.Row;
035:        import org.xorm.util.FieldDescriptor;
036:        import org.xorm.util.TypeConverter;
037:
038:        import javax.jdo.InstanceCallbacks;
039:        import javax.jdo.PersistenceManager;
040:        import javax.jdo.JDOUserException;
041:        import javax.jdo.spi.PersistenceCapable;
042:
043:        import net.sf.cglib.Enhancer;
044:        import net.sf.cglib.Factory;
045:        import net.sf.cglib.MethodFilter;
046:        import net.sf.cglib.MethodInterceptor;
047:        import net.sf.cglib.MethodProxy;
048:
049:        /**
050:         * Handles calls to an interface of the object model.  The interface may
051:         * be defined as a Java interface or a Java abstract class.
052:         * 
053:         * This class ties the notion of an object with state (ObjectState)
054:         * with the concept of the XORM Datastore Row.
055:         *
056:         */
057:        // TODO: Factor out JDO dependencies (make a subclass)
058:        public class InterfaceInvocationHandler extends ObjectState implements 
059:                MethodInterceptor {
060:            /** The logger that this class will output to. */
061:            private static Logger logger = Logger
062:                    .getLogger("org.xorm.InterfaceInvocationHandler");
063:
064:            /**
065:             * Because calls to hashCode() and equals() are handled by this
066:             * class, the static initializer performs reflection on the 
067:             * java.lang.Object class to get instances of the Method objects
068:             * for these two methods so that the reflection API does not need
069:             * to be called each time.
070:             */
071:            private static final Method HASH_CODE, EQUALS, TO_STRING;
072:            private static MethodFilter METHOD_FILTER;
073:
074:            /**
075:             * Initializes the HASH_CODE and EQUALS Method references.
076:             */
077:            static {
078:                Method hashCode = null, equals = null, toString = null;
079:                try {
080:                    hashCode = Object.class.getDeclaredMethod("hashCode", null);
081:                    equals = Object.class.getDeclaredMethod("equals",
082:                            new Class[] { Object.class });
083:                    toString = Object.class.getDeclaredMethod("toString", null);
084:                } catch (NoSuchMethodException willNeverHappen) {
085:                }
086:                HASH_CODE = hashCode;
087:                EQUALS = equals;
088:                TO_STRING = toString;
089:
090:                METHOD_FILTER = new MethodFilter() {
091:                    /**
092:                     * Implements the CGLIB MethodFilter interface and
093:                     * indicates which methods are eligible for
094:                     * enhancement.
095:                     */
096:                    public boolean accept(Member member) {
097:                        return Modifier.isAbstract(member.getModifiers())
098:                                || HASH_CODE.equals(member)
099:                                || EQUALS.equals(member)
100:                                || TO_STRING.equals(member)
101:                                || "clone".equals(member.getName());
102:                    }
103:                };
104:            }
105:
106:            /**
107:             * Gets the InterfaceInvocationHandler associated with the object.
108:             */
109:            public static InterfaceInvocationHandler getHandler(Object object) {
110:                try {
111:                    if (object instanceof  InterfaceInvocationHandler) {
112:                        return (InterfaceInvocationHandler) object;
113:                    } else if (object instanceof  Factory) {
114:                        return (InterfaceInvocationHandler) ((Factory) object)
115:                                .interceptor();
116:                    }
117:                    return null;
118:                } catch (Throwable t) {
119:                    t.printStackTrace();
120:                    throw new Error(t.getClass().getName() + ":"
121:                            + object.getClass().getName() + ":"
122:                            + t.getMessage());
123:                }
124:            }
125:
126:            // The InterfaceManagerFactory that created this instance.
127:            private InterfaceManagerFactory factory;
128:
129:            // The transaction this object is operating within, or null
130:            private TransactionImpl txn;
131:
132:            // The working copy of the Row, containing transactional values
133:            private Row row;
134:
135:            // The datastore's version of the Row at the beginning of the transaction,
136:            // or perhaps null.
137:            private Row snapshotRow;
138:
139:            private ClassMapping mapping;
140:
141:            // The ObjectId for this instance
142:            private Object primaryKey;
143:
144:            // Initially starts off empty, gets filled in as references
145:            // are resolved, gets cleared when objects becomes hollow
146:            // The references held are proxy objects
147:            // or possibly RelationshipProxies, not native types.
148:            private HashMap fieldToValue = new HashMap();
149:
150:            public InterfaceInvocationHandler(InterfaceManagerFactory factory,
151:                    ClassMapping mapping, Row row) {
152:                super (null);
153:                this .factory = factory;
154:                this .mapping = mapping;
155:                this .row = row;
156:                if (row == null) {
157:                    primaryKey = TransientKey.next();
158:                    setStatus(STATUS_TRANSIENT);
159:                } else {
160:                    primaryKey = row.getPrimaryKeyValue();
161:                    setStatus(row.isHollow() ? STATUS_HOLLOW
162:                            : STATUS_PERSISTENT_NONTRANSACTIONAL);
163:                }
164:            }
165:
166:            /** Accessor for persistence manager factory. */
167:            InterfaceManagerFactory getFactory() {
168:                return factory;
169:            }
170:
171:            /** Called during a refresh operation. */
172:            void resetRow(Row row) {
173:                this .row = row;
174:                fieldToValue.clear();
175:                primaryKey = row.getPrimaryKeyValue();
176:                if (txn != null) {
177:                    setStatus(STATUS_PERSISTENT_CLEAN);
178:                } else {
179:                    setStatus(STATUS_PERSISTENT_NONTRANSACTIONAL);
180:                }
181:            }
182:
183:            /** The InterfaceManager that is managing this proxy. */
184:            public InterfaceManager getInterfaceManager() {
185:                return (txn == null) ? null : (InterfaceManager) txn
186:                        .getPersistenceManager();
187:            }
188:
189:            /**
190:             * The ClassMapping that describes how the proxy data translates
191:             * into the datastore Row.
192:             */
193:            public ClassMapping getClassMapping() {
194:                return mapping;
195:            }
196:
197:            // TODO: combine makeTransactional and enterTransaction
198:            /**
199:             * Called only from InterfaceManager.makeTransactional(obj)
200:             */
201:            void makeTransactional(InterfaceManager mgr) {
202:                TransactionImpl t = (TransactionImpl) mgr.currentTransaction();
203:                enterTransaction(t);
204:
205:                if (isHollow()) {
206:                    refresh(mgr);
207:                }
208:                setTransactional(true);
209:            }
210:
211:            /**
212:             * This is the only method where the txn field gets set.
213:             * All reachable collection references become transactional
214:             * under the same transaction.  Additionally, 
215:             * persistent-non-transactional objects become persistent-clean.
216:             */
217:            public void enterTransaction(TransactionImpl txn) {
218:                this .txn = txn;
219:                txn.attach(proxy);
220:                if (row != null) {
221:                    row.clean();
222:                }
223:
224:                // Make transactional via reachability (FIXME)
225:                Iterator i = fieldToValue.values().iterator();
226:                while (i.hasNext()) {
227:                    Object o = i.next();
228:                    if (o instanceof  RelationshipProxy) {
229:                        txn.attachRelationship((RelationshipProxy) o);
230:                    }
231:                }
232:
233:                switch (getStatus()) {
234:                case STATUS_PERSISTENT_NONTRANSACTIONAL:
235:                    if (txn.isActive()) {
236:                        setStatus(STATUS_PERSISTENT_CLEAN);
237:                    }
238:                    break;
239:                }
240:            }
241:
242:            /**
243:             * @return true if the handler should be detached from the transaction.
244:             */
245:            public boolean exitTransaction(boolean commit) {
246:                boolean retainValues = txn.getRetainValues();
247:                boolean detach = false;
248:
249:                // On commit, the following state transitions occur:
250:                //   TRANSIENT --> TRANSIENT
251:                //   HOLLOW --> HOLLOW
252:                //   TRANSIENT_CLEAN --> TRANSIENT_CLEAN
253:                //   TRANSIENT_DIRTY --> TRANSIENT_CLEAN
254:                //   PERSISTENT_(NEW_)?DELETED --> TRANSIENT
255:                //   PERSISTENT_NONTRANSACTIONAL --> PERSISTENT_NONTRANSACTIONAL
256:
257:                // commit with retainValues == true:
258:                //   PERSISTENT_(NEW|CLEAN|DIRTY) --> PERSISTENT_NONTRANSACTIONAL
259:
260:                // commit with retainValues == false
261:                //   PERSISTENT_(NEW|CLEAN|DIRTY) --> HOLLOW
262:
263:                // rollback differs:
264:                //   PERSISTENT_NEW --> TRANSIENT
265:                //   PERSISTENT_DELETED --> HOLLOW if retainValues == false
266:                //   PERSISTENT_DELETED --> PERSISTENT_NONTRANSACTIONAL if true
267:
268:                if (commit) {
269:                    switch (status) {
270:                    case STATUS_TRANSIENT_DIRTY:
271:                        setStatus(STATUS_TRANSIENT_CLEAN);
272:                        break;
273:                    case STATUS_PERSISTENT_NEW_DELETED:
274:                    case STATUS_PERSISTENT_DELETED:
275:                        setStatus(STATUS_TRANSIENT);
276:                        detach = true;
277:                        txn = null;
278:                        break;
279:                    case STATUS_PERSISTENT_NEW:
280:                    case STATUS_PERSISTENT_CLEAN:
281:                    case STATUS_PERSISTENT_DIRTY:
282:                        if (retainValues) {
283:                            setStatus(STATUS_PERSISTENT_NONTRANSACTIONAL);
284:                        } else {
285:                            setStatus(STATUS_HOLLOW);
286:                        }
287:                        break;
288:                    }
289:                } else {
290:                    // Rollback
291:                    if (snapshotRow != null) {
292:                        row = snapshotRow;
293:                    }
294:                    // Cached object references may be invalid
295:                    fieldToValue = new HashMap();
296:                    switch (status) {
297:                    case STATUS_TRANSIENT_DIRTY:
298:                        setStatus(STATUS_TRANSIENT_CLEAN);
299:                        break;
300:                    case STATUS_PERSISTENT_NEW:
301:                    case STATUS_PERSISTENT_NEW_DELETED:
302:                        setStatus(STATUS_TRANSIENT);
303:                        detach = true;
304:                        txn = null;
305:                        break;
306:                    case STATUS_PERSISTENT_CLEAN:
307:                    case STATUS_PERSISTENT_DIRTY:
308:                    case STATUS_PERSISTENT_DELETED:
309:                        if (retainValues) {
310:                            setStatus(STATUS_PERSISTENT_NONTRANSACTIONAL);
311:                        } else {
312:                            setStatus(STATUS_HOLLOW);
313:                        }
314:                        break;
315:                    }
316:                }
317:                snapshotRow = null;
318:                return detach;
319:            }
320:
321:            // TODO: This is currently unused.
322:            private void makeHollow() {
323:                if (proxy instanceof  InstanceCallbacks) {
324:                    ((InstanceCallbacks) proxy).jdoPreClear();
325:                }
326:
327:                primaryKey = getRow().getPrimaryKeyValue();
328:                fieldToValue = new HashMap();
329:                status = STATUS_HOLLOW;
330:                row = null;
331:            }
332:
333:            // Causes a state change
334:            // This is only called from invokeSet(), so we can be assured
335:            // that the object has been entered into the current transaction
336:            // if necessary.
337:            public void makeDirty() {
338:                switch (status) {
339:                case STATUS_PERSISTENT_CLEAN:
340:                    status = STATUS_PERSISTENT_DIRTY;
341:                    break;
342:                case STATUS_TRANSIENT_CLEAN:
343:                    if (txn != null && txn.isActive()) {
344:                        // Enlist in transaction
345:                        enterTransaction(txn);
346:                        status = STATUS_TRANSIENT_DIRTY;
347:                    }
348:                    break;
349:                case STATUS_PERSISTENT_DELETED:
350:                case STATUS_PERSISTENT_NEW_DELETED:
351:                    throw new JDOUserException(
352:                            "cannot change field of deleted object");
353:                }
354:                if (txn != null) {
355:                    snapshot();
356:                }
357:            }
358:
359:            /**
360:             * Marks this object and all the objects it contains as persistent
361:             * (persistence by reachability).  Any newly persistent objects will
362:             * be inserted into the database when the transaction is committed.
363:             */
364:            public void makePersistent(InterfaceManager mgr) {
365:                if (txn != null) {
366:                    // Object was transactional already
367:                    if (mgr == txn.getInterfaceManager())
368:                        return;
369:                    else
370:                        throw new JDOUserException("Object " + toString()
371:                                + " managed by other PersistenceManager");
372:                }
373:                if (!isPersistent()) {
374:                    enterTransaction((TransactionImpl) mgr.currentTransaction());
375:                    status = STATUS_PERSISTENT_NEW;
376:
377:                    // Follow any references and mark those as persistent
378:                    Iterator i = mapping.getRelationships().keySet().iterator();
379:                    while (i.hasNext()) {
380:                        String field = (String) i.next();
381:                        RelationshipMapping rm = mapping.getRelationship(field);
382:                        if (rm.getTarget() != null) {
383:                            // It's a to-many relationship
384:                            Collection c = invokeCollectionGet(field, rm, null,
385:                                    null);
386:                            Iterator j = c.iterator();
387:                            while (j.hasNext()) {
388:                                Object o = j.next();
389:                                InterfaceInvocationHandler other = getHandler(o);
390:                                other.makePersistent(mgr);
391:                            }
392:                        } else {
393:                            Class returnType = rm.getSource().getElementClass();
394:                            ClassMapping returnTypeMapping = factory
395:                                    .getModelMapping().getClassMapping(
396:                                            returnType);
397:                            Object o = invokeGet(field, returnTypeMapping,
398:                                    returnType);
399:                            if (o != null) {
400:                                // Get the handler for o
401:                                InterfaceInvocationHandler other = getHandler(o);
402:                                other.makePersistent(mgr);
403:                            }
404:                        }
405:                    }
406:                }
407:            }
408:
409:            // Another object, which might be one this references, has had
410:            // its ID changed (possibly through an insert)
411:            public void notifyIDChanged(Object oldID, Object newID) {
412:                // See if this has a transient reference that gets to other
413:                Iterator i = mapping.getRelationships().keySet().iterator();
414:                while (i.hasNext()) {
415:                    String field = (String) i.next();
416:                    Object proxy = fieldToValue.get(field);
417:                    if (proxy == null)
418:                        continue;
419:                    if (proxy instanceof  RelationshipProxy) {
420:                        RelationshipProxy rp = (RelationshipProxy) fieldToValue
421:                                .get(field);
422:                        rp.notifyIDChanged(oldID, newID);
423:                    } else {
424:                        Column column = mapping.getColumn(field);
425:                        Object value = getRow().getValue(column);
426:                        if (oldID.equals(value)) {
427:                            getRow().setValue(column, newID);
428:                        }
429:                    }
430:                }
431:            }
432:
433:            public int compareTo(InterfaceInvocationHandler other) {
434:                // For convenient sorting, sort first by mapped class,
435:                // then by object id
436:                if (other.mapping.equals(mapping)) {
437:                    if (!((other.primaryKey instanceof  TransientKey) ^ (primaryKey instanceof  TransientKey))) {
438:                        return ((Comparable) primaryKey)
439:                                .compareTo(other.primaryKey);
440:                    } else {
441:                        if (other.primaryKey instanceof  TransientKey)
442:                            return 1;
443:                        return -1;
444:                    }
445:                }
446:                return mapping.getMappedClass().getName().compareTo(
447:                        other.mapping.getMappedClass().getName());
448:            }
449:
450:            /**
451:             * Returns true if any of the references from this object resolve
452:             * to the object supplied as a parameter.
453:             */
454:            public boolean dependsOn(InterfaceInvocationHandler other) {
455:                if (other == this )
456:                    return true;
457:
458:                // See if this has a reference that gets to other
459:                Iterator i = mapping.getRelationships().keySet().iterator();
460:                while (i.hasNext()) {
461:                    String field = (String) i.next();
462:                    Column column = mapping.getColumn(field);
463:                    if (column != null) {
464:                        Object value = getRow().getValue(column);
465:                        if (other.primaryKey.equals(value)) {
466:                            return true;
467:                        } else if (value instanceof  TransientKey) {
468:                            // Transient key to something else
469:                            Class returnType = mapping.getRelationship(field)
470:                                    .getSource().getElementClass();
471:                            ClassMapping returnTypeMapping = factory
472:                                    .getModelMapping().getClassMapping(
473:                                            returnType);
474:                            Object o = invokeGet(field, returnTypeMapping,
475:                                    returnType);
476:                            if (o != null) {
477:                                // Get the handler for o
478:                                InterfaceInvocationHandler o2 = getHandler(o);
479:                                if (o2.dependsOn(other))
480:                                    return true;
481:                            }
482:                        } // value instanceof TransientKey
483:                    } // column != null
484:                    /* THIS SECTION NOT USED-- column dependence only
485:                       else {
486:                       // Relationship without a column (-to-many?)
487:                       Object value = fieldToValue.get(field);
488:                       if (value instanceof RelationshipProxy) {
489:                       if (((RelationshipProxy) value).dependsOn(other)) {
490:                       return true;
491:                       }
492:                       }
493:                       }
494:                     */
495:                } // for each relationship identified in class mapping
496:                return false;
497:            } // dependsOn
498:
499:            public Object getObjectId() {
500:                return primaryKey;
501:            }
502:
503:            public void refreshObjectId() {
504:                primaryKey = getRow().getPrimaryKeyValue();
505:            }
506:
507:            public void refresh(InterfaceManager mgr) {
508:                row = mgr.lookupRow(mapping, primaryKey);
509:
510:                if (proxy instanceof  InstanceCallbacks) {
511:                    ((InstanceCallbacks) proxy).jdoPostLoad();
512:                }
513:                if (status == STATUS_HOLLOW) {
514:                    // Not currently in a transaction
515:                    if (mgr.currentTransaction().isActive()) {
516:                        enterTransaction((TransactionImpl) mgr
517:                                .currentTransaction());
518:                    } else {
519:                        // this was a nontransactional read
520:                        status = STATUS_PERSISTENT_NONTRANSACTIONAL;
521:                        return;
522:                    }
523:                }
524:                status = STATUS_PERSISTENT_CLEAN;
525:            }
526:
527:            /** Clones the current backing row for use in case of rollback. */
528:            public void snapshot() {
529:                snapshotRow = (Row) getRow().clone();
530:            }
531:
532:            /** Initializes or retrieves the working Row. */
533:            public Row getRow() {
534:                if (row == null) {
535:                    row = new Row(mapping.getTable());
536:                    initDefaultValues();
537:                }
538:                return row;
539:            }
540:
541:            /** Sets the working Row. */
542:            public void setRow(Row newRow) {
543:                row = newRow;
544:            }
545:
546:            /**
547:             * Initialize any primitive values that should be populated
548:             * in the row when newly created.
549:             */
550:            private void initDefaultValues() {
551:                Iterator i = mapping.getMappedFieldDescriptors().iterator();
552:                while (i.hasNext()) {
553:                    FieldDescriptor fd = (FieldDescriptor) i.next();
554:                    Column c = mapping.getColumn(fd.name);
555:                    if (!c.isReadOnly()) {
556:                        row.setValue(c,
557:                                fd.type.isPrimitive() ? defaultValue(fd.type)
558:                                        : null);
559:                    }
560:                }
561:            }
562:
563:            /** 
564:             * Returns a debug representation of this handler, in the format
565:             * [org.xorm.InterfaceInvocationHandler@xxxxxx; interface com.xyz.model.MyClass, primaryKey: {id}, status: PERSISTENT_CLEAN]
566:             */
567:            public String toString() {
568:                return new StringBuffer("[").append(super .toString()).append(
569:                        "; ").append(mapping.getMappedClass()).append(
570:                        ", primaryKey: ").append(primaryKey).append(
571:                        ", status: ").append(getStatusName()).append(']')
572:                        .toString();
573:            }
574:
575:            private boolean checkEquals(Object me, Object other) {
576:                if (me == other) {
577:                    return true;
578:                } else if (other == null) {
579:                    return false;
580:                } else {
581:                    if (me.getClass().equals(other.getClass())) {
582:                        InterfaceInvocationHandler otherHandler = getHandler(other);
583:                        if (primaryKey.equals(otherHandler.primaryKey)) {
584:                            // Added these hollow checks (Dan)
585:                            // Otherwise, a hollow object would fail checkEquals
586:                            // against a non-hollow object representing the same
587:                            // row with the same values.   Gotta refresh to make
588:                            // sure the Row's values will be populated.
589:                            if (isHollow()) {
590:                                refresh(txn.getInterfaceManager());
591:                            }
592:                            if (otherHandler.isHollow()) {
593:                                otherHandler.refresh(otherHandler.txn
594:                                        .getInterfaceManager());
595:                            }
596:
597:                            Row myRow = row;
598:                            Row otherRow = otherHandler.row;
599:                            if (!row.equals(otherHandler.row)) {
600:                                return false;
601:                            }
602:                            // TODO check relationships?
603:                            return true;
604:                        }
605:                    }
606:                }
607:                return false;
608:            }
609:
610:            /**
611:             * Constructs a new proxy instance for the specified interface.
612:             * The proxy will implement or extend the specified class and
613:             * also implement javax.jdo.spi.PersistenceCapable.
614:             */
615:            Object newProxy() {
616:                Class clazz = mapping.getMappedClass();
617:                Factory sample = (Factory) factory.getSample(clazz);
618:                if (sample != null) {
619:                    proxy = sample.newInstance(this );
620:                    setProxy(proxy);
621:                    return proxy;
622:                }
623:                Class[] interfaces;
624:                ClassLoader loader = clazz.getClassLoader();
625:
626:                if (clazz.isInterface()) {
627:                    interfaces = new Class[] { clazz, PersistenceCapable.class };
628:                    clazz = null;
629:                } else {
630:                    interfaces = new Class[] { PersistenceCapable.class };
631:                }
632:
633:                Object proxy;
634:                try {
635:                    proxy = Enhancer.enhance(clazz, interfaces, this , loader,
636:                            null, METHOD_FILTER);
637:                } catch (Error e) {
638:                    throw (Error) e.fillInStackTrace();
639:                } catch (Throwable t) {
640:                    t.printStackTrace();
641:                    throw new Error(t.getMessage());
642:                }
643:                setProxy(proxy);
644:                factory.putSample(mapping.getMappedClass(), proxy);
645:                return proxy;
646:            }
647:
648:            /**
649:             * This method is invoked after execution, or in the case of 
650:             * abstract or interface methods, instead of.
651:             *
652:             * @param proxy this
653:             * @param method Method
654:             * @param args Arg array
655:             * @param methodProxy the MethodProxy
656:             * @throws Throwable any exception
657:             * @return value to return from generated method
658:             */
659:            public Object intercept(Object proxy, Method method, Object[] args,
660:                    MethodProxy methodProxy) throws Throwable {
661:                if (method.equals(HASH_CODE)) {
662:                    return new Integer(primaryKey.hashCode());
663:                } else if (method.equals(EQUALS)) {
664:                    return Boolean.valueOf(checkEquals(proxy, args[0]));
665:                } else if (method.equals(TO_STRING)) {
666:                    return toString();
667:                }
668:
669:                if ("clone".equals(method.getName())) {
670:                    InterfaceInvocationHandler handler = new InterfaceInvocationHandler(
671:                            factory, mapping, null);
672:                    handler.row = this .row;
673:                    handler.row.setPrimaryKeyValue(null);
674:                    return handler.newProxy();
675:                }
676:
677:                // Calls to methods of PersistenceCapable require us to 
678:                // make a suitable PersistenceCapableImpl.
679:                if (method.getDeclaringClass().equals(PersistenceCapable.class)) {
680:                    PersistenceCapableImpl pc = new PersistenceCapableImpl(this );
681:                    return method.invoke(pc, args);
682:                }
683:
684:                // Force HOLLOW instances to be read
685:                if (isHollow()) {
686:                    refresh(txn.getInterfaceManager());
687:                } else if (getStatus() == STATUS_PERSISTENT_NONTRANSACTIONAL) {
688:                    // Account for thread-local transactions if necessary
689:                    txn = (TransactionImpl) txn.getInterfaceManager()
690:                            .currentTransaction();
691:                    if (txn.isActive()) {
692:                        // nontransactional instances become transactional if
693:                        // there is an active transaction.
694:                        // TODO: This should also refresh() the instance
695:                        enterTransaction(txn);
696:                    }
697:                }
698:
699:                String field = mapping.getFieldForMethod(method);
700:                Column c = mapping.getColumnForMethod(method);
701:                if (c == null) {
702:                    RelationshipMapping rm = mapping
703:                            .getRelationshipForMethod(method);
704:                    if (rm == null)
705:                        return null;
706:                    if (args == null || args.length == 0) {
707:                        // Relationship read method
708:                        return invokeCollectionGet(field, rm, method
709:                                .getReturnType(), null);
710:                    } else if (method.getName().startsWith("set")) {
711:                        invokeCollectionSet(field, rm, method.getReturnType(),
712:                                (Collection) args[0]);
713:                        return null;
714:                    } else {
715:                        // Relationship read method with arguments...this would
716:                        // be the case when a filtered relationship is being invoked,
717:                        // i.e. when the relationship collection has been configured
718:                        // with a filter and possibly parameters and variables.
719:                        // Most likely since parameters are being passed that's the
720:                        // case here.
721:                        return invokeCollectionGet(field, rm, method
722:                                .getReturnType(), args);
723:                    }
724:                }
725:
726:                // Method maps to get/set on a column
727:                if (args == null || args.length == 0) {
728:                    // Get method
729:                    Class returnType = method.getReturnType();
730:                    ClassMapping returnTypeMapping = null;
731:                    if (ClassMapping.isUserType(returnType)) {
732:                        returnTypeMapping = factory.getModelMapping()
733:                                .getClassMapping(returnType);
734:                    }
735:                    return invokeGet(field, returnTypeMapping, returnType);
736:                } else {
737:                    // Write method
738:                    Object value = args[0];
739:                    invokeSet(field, c, value);
740:                }
741:                return null;
742:            }
743:
744:            private void invokeSet(String field, Column c, Object value) {
745:
746:                // TO THINK ABOUT: should a set of a new parent detach the
747:                // pre-existing relationship if one is there?  If so, need
748:                // to check for that old value first.
749:
750:                if (value != null) {
751:                    if (value instanceof  PersistenceCapable) { // TODO: use cglib interface?
752:                        fieldToValue.put(field, value);
753:                        // Convert value to primary key:
754:                        InterfaceInvocationHandler other = getHandler(value);
755:                        // If this is persistent, anything attached
756:                        // becomes persistent too
757:                        if (isPersistent()) {
758:                            other.makePersistent(txn.getInterfaceManager());
759:                        }
760:                        value = other.primaryKey;
761:
762:                        String inverse = mapping.getInverse(field);
763:                        if (inverse != null) {
764:                            logger.info("Examining inverse relationship");
765:                            // Is it a collection or a single item?
766:                            RelationshipMapping rm = other.mapping
767:                                    .getRelationship(inverse);
768:                            int type = rm.getSource().getCollectionType();
769:                            if (type == RelationshipMapping.Endpoint.SET) {
770:                                // Add this object to the relationship
771:                                // TODO
772:                            } else {
773:                                // Call set on other object
774:                                // TODO
775:                            }
776:                        }
777:                    }
778:                }
779:
780:                Row theRow = getRow();
781:                // Gotta see if we're dealing with a cached instance of the Row
782:                if (theRow.isCached()) {
783:                    // Yep, let's not modify that instance...now we need to clone.
784:                    theRow = (Row) theRow.clone();
785:                    // Make sure the cached flag is set to false on our copy
786:                    theRow.setCached(false);
787:                    // Update our instance variable or whatever
788:                    setRow(theRow);
789:                }
790:
791:                if (txn != null && txn.isActive() && !isDirty()) {
792:                    makeDirty();
793:                }
794:                theRow.setValue(c, value);
795:            }
796:
797:            private Collection invokeCollectionGet(String field,
798:                    RelationshipMapping mapping, Class returnType, Object[] args) {
799:                //logger.info("invokeCollectionGet: I am " + toString());
800:                RelationshipProxy rp = null;
801:                if (fieldToValue.containsKey(field)) {
802:                    rp = (RelationshipProxy) fieldToValue.get(field);
803:                    //logger.info("Using existing value for " + field + " with rp " + rp);
804:                } else {
805:                    /*
806:                      if ((returnType != null) && List.class.isAssignableFrom(returnType)) {
807:                      rp = new ListProxy(mgr, mapping, this, field, args);
808:                      } else {
809:                     */
810:                    InterfaceManager mgr = null;
811:                    if (txn != null) {
812:                        mgr = txn.getInterfaceManager();
813:                    }
814:                    rp = new RelationshipProxy(mgr, mapping, this , factory
815:                            .getModelMapping().getClassMapping(
816:                                    mapping.getSource().getElementClass()),
817:                            args);
818:                    /*
819:                      }
820:                     */
821:                    if (isPersistent()) {
822:                        if (txn != null && txn.isActive()) {
823:                            txn.attachRelationship(rp);
824:                        }
825:                    }
826:                    //logger.info("Calling fieldToValue.put " + field + " with rp " + rp);
827:
828:                    // Do not cache filtered relationships locally
829:                    if (mapping.getFilter() == null) {
830:                        fieldToValue.put(field, rp);
831:                    }
832:                }
833:                return rp;
834:            }
835:
836:            private void invokeCollectionSet(String field,
837:                    RelationshipMapping mapping, Class returnType,
838:                    Collection coll) {
839:                //  Sanity checking
840:                if (coll == null) {
841:                    throw new NullPointerException(
842:                            "Setting collection to null is bad idea, "
843:                                    + "if you need to clear this relationschip try to make this collection empty.");
844:                }
845:                if (coll instanceof  RelationshipProxy) {
846:                    // check if it is already set
847:                    Object obj = fieldToValue.get(field);
848:                    if (coll.equals(obj)) {
849:                        // we do not need to set back the same collection
850:                        return;
851:                    } else {
852:                        // import relationship if allowed
853:                        if (mapping.isMToN()) {
854:                            //is many to many, add all
855:                            RelationshipProxy rp = (RelationshipProxy) invokeCollectionGet(
856:                                    field, mapping, returnType, null);
857:                            rp.clear();
858:                            rp.addAll(coll);
859:                            return;
860:                        } else {
861:                            throw new JDOUserException(
862:                                    "by one to many relation you can not share referenced objects");
863:                        }
864:                    }
865:                } else {
866:                    //collection is not backed in datastore
867:                    //TODO: try to optimize adding collection, replace addAll(Collection) with something better 
868:                    RelationshipProxy rp = (RelationshipProxy) invokeCollectionGet(
869:                            field, mapping, returnType, null);
870:                    rp.clear();
871:                    rp.addAll(coll);
872:                }
873:            }
874:
875:            /**
876:             * Returns the object associated with the given field that
877:             * is of the given return type.
878:             */
879:            public Object invokeGet(String field,
880:                    ClassMapping returnTypeMapping, Class returnType) {
881:                Column c = mapping.getColumn(field);
882:                if (c == null)
883:                    throw new JDOUserException("No column for field " + field
884:                            + " of class " + mapping.getMappedClass().getName());
885:                if (!getRow().containsValue(c)) {
886:                    // Column fault: column was not in default fetch group
887:                    DataFetchGroup dfg = new DataFetchGroup();
888:                    dfg.addColumn(c);
889:                    if (getRow().isCached()) {
890:                        // We need to clone the row so we don't modify the cached Row
891:                        Row theRow = (Row) getRow().clone();
892:                        theRow.setCached(false);
893:                        setRow(theRow);
894:                    }
895:                    txn.getInterfaceManager().refreshColumns(getRow(), dfg);
896:                }
897:                Object value = getRow().getValue(c);
898:                if (value == null) {
899:                    return null;
900:                }
901:                if (returnTypeMapping != null) {
902:                    if (fieldToValue.containsKey(field)) {
903:                        value = fieldToValue.get(field);
904:                    } else {
905:                        value = txn.getInterfaceManager().lookup(
906:                                returnTypeMapping, value);
907:                        InterfaceInvocationHandler other = getHandler(value);
908:                        fieldToValue.put(field, value);
909:                    }
910:                }
911:
912:                if (returnType != null) {
913:                    value = TypeConverter.convertToType(value, returnType);
914:                }
915:                return value;
916:            }
917:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.