Source Code Cross Referenced for Store.java in  » Database-ORM » beankeeper » hu » netmind » persistence » 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 » beankeeper » hu.netmind.persistence 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * Copyright (C) 2006 NetMind Consulting Bt.
0003:         *
0004:         * This library is free software; you can redistribute it and/or
0005:         * modify it under the terms of the GNU Lesser General Public
0006:         * License as published by the Free Software Foundation; either
0007:         * version 3 of the License, or (at your option) any later version.
0008:         *
0009:         * This library is distributed in the hope that it will be useful,
0010:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:         * Lesser General Public License for more details.
0013:         *
0014:         * You should have received a copy of the GNU Lesser General Public
0015:         * License along with this library; if not, write to the Free Software
0016:         * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:         */package hu.netmind.persistence;
0018:
0019:        import org.apache.log4j.Logger;
0020:        import java.util.*;
0021:        import hu.netmind.persistence.parser.*;
0022:        import hu.netmind.persistence.node.*;
0023:        import hu.netmind.persistence.event.*;
0024:        import javax.sql.DataSource;
0025:
0026:        /**
0027:         * This store class is the entry point to the persistence library. To
0028:         * store, remove or select given objects, just use the appropriate
0029:         * method.
0030:         * @author Brautigam Robert
0031:         * @version Revision: $Revision$
0032:         */
0033:        public class Store {
0034:            private static Logger logger = Logger.getLogger(Store.class);
0035:
0036:            private StoreContext context;
0037:            private Thread shutdownHook;
0038:
0039:            /**
0040:             * Get context.
0041:             */
0042:            protected StoreContext getContext() {
0043:                return context;
0044:            }
0045:
0046:            /**
0047:             * Instantiate a store. Note that you should only make one
0048:             * store instance for each datasource you might want to use, all
0049:             * methods are thread-safe, so you can use the single instance 
0050:             * in more threads.
0051:             * @param driverClass The driver class to register.
0052:             * @param url The driver's jdbc url (including username and password).
0053:             */
0054:            public Store(String driverClass, String url) {
0055:                // {{{ Constructor
0056:                this (new DriverDataSource(driverClass, url));
0057:                // }}}
0058:            }
0059:
0060:            /**
0061:             * Instantiate a store. Note that you should only make one
0062:             * store instance for each datasource you might want to use, all
0063:             * methods are thread-safe, so you can use the single instance 
0064:             * in more threads.
0065:             */
0066:            public Store(DataSource dataSource) {
0067:                // {{{ Create store context
0068:                // Note that initialization order is important!
0069:                context = new StoreContext();
0070:                context.setEventDispatcher(new EventDispatcher());
0071:                context.setStore(this );
0072:                context.setSerialTracker(new SerialTracker());
0073:                context.setDatabase(DatabaseFactory.getDatabase(dataSource));
0074:                context.setTypeHandlerTracker(new TypeHandlerTracker(context));
0075:                context.setTransactionTracker(new TransactionTracker(context));
0076:                context.setLockTracker(new LockTracker(context));
0077:                context.setNodeManager(new NodeManager(context));
0078:                context.setClassTracker(new ClassTracker(context));
0079:                context.setObjectTracker(new ObjectTracker(context));
0080:                context.setCache(new ResultsCache(context));
0081:                // Start node as soon as all subsystems run
0082:                try {
0083:                    context.getNodeManager().ensureState(
0084:                            NodeManager.STATE_CONNECTED); // Init NodeManager
0085:                } catch (Exception e) {
0086:                    logger
0087:                            .warn(
0088:                                    "initialization failed, will try to establish contact later.",
0089:                                    e);
0090:                }
0091:                // Proper shutdown
0092:                shutdownHook = new Thread(new ShutdownProcess());
0093:                Runtime.getRuntime().addShutdownHook(shutdownHook);
0094:                /// }}}
0095:            }
0096:
0097:            /**
0098:             * Close the store, and release all resources. This method is automatically
0099:             * invoked as a JVM shutdown hook, so it does not have to be called at all.
0100:             */
0101:            public void close() {
0102:                close(false);
0103:            }
0104:
0105:            /**
0106:             * Internal close method.
0107:             */
0108:            private void close(boolean shutdown) {
0109:                logger.debug("store shutdown running");
0110:                context.getNodeManager().close();
0111:                context.getDatabase().release();
0112:                if (!shutdown)
0113:                    Runtime.getRuntime().removeShutdownHook(shutdownHook); // Do not run twice
0114:            }
0115:
0116:            /**
0117:             * Get the transaction tracker associated with this store. The transaction
0118:             * tracker can be used to create transactions, and listen for transactional
0119:             * events.
0120:             * @return The transaction tracker.
0121:             */
0122:            public TransactionTracker getTransactionTracker() {
0123:                return context.getTransactionTracker();
0124:            }
0125:
0126:            /**
0127:             * Get the lock tracker. This tracker can be used to lock and unlock
0128:             * objects for exclusive operations.
0129:             */
0130:            public LockTracker getLockTracker() {
0131:                return context.getLockTracker();
0132:            }
0133:
0134:            /**
0135:             * Get the event dispatcher in which the caller may register
0136:             * listeners.
0137:             */
0138:            public EventDispatcher getEventDispatcher() {
0139:                return context.getEventDispatcher();
0140:            }
0141:
0142:            /**
0143:             * Get the persistence id for an object. This method always returns
0144:             * a valid id, even if the object is not saved, or otherwise has
0145:             * no persistence id. If the object is later saved, this persistence id
0146:             * is always preserved.<br>
0147:             * Same as: <code>getPersistenceMetaData(obj).getPersistenceId()</code>.
0148:             * @return A persistence id, and never null.
0149:             */
0150:            public Long getPersistenceId(Object obj) {
0151:                return getPersistenceMetaData(obj).getPersistenceId();
0152:            }
0153:
0154:            /**
0155:             * Get the persistence meta-data object for a given object. Metadata
0156:             * is always available, even for non-saved objects.
0157:             */
0158:            public PersistenceMetaData getPersistenceMetaData(Object obj) {
0159:                return context.getObjectTracker().getMetaData(obj);
0160:            }
0161:
0162:            /**
0163:             * Save the given object to the store. The given object's all private
0164:             * non-transient fields will be saved. If the object  was not selected
0165:             * from the store, and not yet saved, it will be created in the store,
0166:             * and a unique id will be assigned, so all subsequent calls to save
0167:             * the given object will only modify the already existing instance in
0168:             * store. A few tips:<br>
0169:             * <ul>
0170:             *    <li>Use simple beans. Although this library does not scan
0171:             *    methods to determine the attributes to save, it is a good
0172:             *    idea to simplify work with them.</li>
0173:             *    <li>If you do not use simple beans, watch out that your
0174:             *    object does not reference unnecessary objects, because
0175:             *    if it does, all will be saved/inserted and tracked.</li>
0176:             *    <li>You CAN use objects which reference other beans though.
0177:             *    But beware, that all objects which are directly referenced
0178:             *    will be loaded when the parent object loads.</li>
0179:             *    <li>You CAN use Map, and List types in your
0180:             *    beans. Check the documentation.</li>
0181:             * </ul>
0182:             * <i>Note:</i>A save operation is not recursive. It does not traverse
0183:             * the object hierarchy, only saves the object given, and does not save
0184:             * objects referenced, except when referenced object does not exist
0185:             * yet. If a referenced object does not yet exist, it will be inserted
0186:             * into database (and recursively all referenced objects of that object).
0187:             * <i>Implementation note:</i>This class is the intelligent part of
0188:             * the framework, an intentionally so. This is the class that coordinates
0189:             * all other classes, trackers and functions together.
0190:             * @param obj The object to save.
0191:             * @throws StoreException If save is not successfull.
0192:             */
0193:            public void save(Object obj) {
0194:                // {{{ Save
0195:                // Transaction
0196:                Transaction transaction = context.getTransactionTracker()
0197:                        .getTransaction(TransactionTracker.TX_REQUIRED);
0198:                transaction.begin();
0199:                transaction.setLastOperation("saving object: " + obj);
0200:                try {
0201:                    Long currentSerial = context.getNodeManager()
0202:                            .getNextSerial();
0203:                    Long currentTxSerial = transaction.getSerial(currentSerial);
0204:                    logger.debug("save called, using serial: " + currentSerial
0205:                            + ", tx serial: " + currentTxSerial);
0206:                    // Save object or insert, possibly recursively.
0207:                    // The waitingObjects list is an ordered list which
0208:                    // contains all objects currently waiting for saving.
0209:                    // First it contains the object to be saved, then if
0210:                    // it referenced more objects, those will be appended to the
0211:                    // list. An object can only be saved, when all referencing objects
0212:                    // are all at least created.
0213:                    LinkedList events = new LinkedList();
0214:                    LinkedList savedHandledObjects = new LinkedList();
0215:                    HashSet waitingObjects = new HashSet();
0216:                    waitingObjects.add(context.getObjectTracker().getWrapper(
0217:                            obj));
0218:                    while (waitingObjects.size() > 0) {
0219:                        if (logger.isDebugEnabled())
0220:                            logger.debug("saving object, waiting list: "
0221:                                    + waitingObjects.size() + ", memory: "
0222:                                    + Runtime.getRuntime().freeMemory());
0223:                        // {{{ Selecting objects which wait for saving
0224:                        // Get the last object (the bottom of dependency tree)
0225:                        ObjectTracker.ObjectWrapper currentWrapper = (ObjectTracker.ObjectWrapper) waitingObjects
0226:                                .iterator().next();
0227:                        Object current = currentWrapper.getObject();
0228:                        // If object does not exists, register it with object
0229:                        // tracker. Note, that object tracker only leases an id
0230:                        // and exists() will return false until object commited.
0231:                        // Also, if object already has an id, this will do nothing.
0232:                        context.getObjectTracker().registerObject(current, 0);
0233:                        long currentId = context.getObjectTracker()
0234:                                .getIdentifier(current);
0235:                        logger.debug("saving object with id: " + currentId);
0236:                        // Lock object
0237:                        context.getLockTracker().lock(current);
0238:                        transaction.addSavedObject(currentWrapper);
0239:                        // Now get object's class info. This method does not
0240:                        // return null. If the class info is not available, it
0241:                        // creates it, if it is available it checks whether to
0242:                        // update the info in database (new attibutes, etc)
0243:                        ClassInfo classInfo = context.getClassTracker()
0244:                                .getClassInfo(current.getClass(), current);
0245:                        logger.debug("object class info is: " + classInfo);
0246:                        // Assemble changed attributes of this object into a Map.
0247:                        // If an attribute is not a primitive type it is checked,
0248:                        // if it exists. If it does not, it is appended to waitingObjects
0249:                        // and we start from beginning with this object. If it exists,
0250:                        // then the reference id will be inserted into the change Map.
0251:                        Map changedAttributes = new HashMap();
0252:                        Map updatedAttributes = new HashMap();
0253:                        // }}}
0254:                        // {{{ Assemble previous state of object
0255:                        Map nonchangedAttributes = new HashMap();
0256:                        if (context.getObjectTracker().exists(current)) {
0257:                            // First, get all original attributes from the database,
0258:                            // because we'll have to create a full row, even when a
0259:                            // single attribute changed.
0260:                            ClassEntry selectClassEntry = classInfo
0261:                                    .getSourceEntry();
0262:                            TableTerm selectTerm = new TableTerm(classInfo
0263:                                    .getTableName(selectClassEntry), null);
0264:                            List relatedClassEntries = context
0265:                                    .getClassTracker().getRelatedClassEntries(
0266:                                            selectClassEntry);
0267:                            TimeControl timeControl = new TimeControl(
0268:                                    currentSerial, currentTxSerial, true);
0269:                            for (int u = 0; u < relatedClassEntries.size(); u++) {
0270:                                ClassEntry relatedClassEntry = (ClassEntry) relatedClassEntries
0271:                                        .get(u);
0272:                                ClassInfo relatedClassInfo = context
0273:                                        .getClassTracker().getClassInfo(
0274:                                                relatedClassEntry);
0275:                                if (relatedClassInfo == null)
0276:                                    throw new ParserException(
0277:                                            ParserException.ABORT,
0278:                                            "object class not found for loading: '"
0279:                                                    + relatedClassEntry + "'");
0280:                                // Create the term and add to mainterm
0281:                                TableTerm leftTerm = new TableTerm(
0282:                                        relatedClassInfo
0283:                                                .getTableName(relatedClassEntry),
0284:                                        null);
0285:                                selectTerm.getLeftTableTerms().add(leftTerm);
0286:                            }
0287:                            Expression expr = new Expression();
0288:                            expr.add(new ReferenceTerm(selectTerm,
0289:                                    "persistence_id"));
0290:                            expr.add("=");
0291:                            expr.add(new ConstantTerm(new Long(currentId)));
0292:                            expr.add("and");
0293:                            timeControl.apply(expr, selectTerm);
0294:                            for (int u = 0; u < selectTerm.getLeftTableTerms()
0295:                                    .size(); u++) {
0296:                                expr.add("and");
0297:                                timeControl.applyToLeftTable(expr,
0298:                                        (TableTerm) selectTerm
0299:                                                .getLeftTableTerms().get(u));
0300:                            }
0301:                            QueryStatement referredStatement = new QueryStatement(
0302:                                    selectTerm, expr, null);
0303:                            referredStatement.setStaticRepresentation("FIND "
0304:                                    + selectClassEntry
0305:                                    + " where persistence_id = " + currentId);
0306:                            referredStatement.setAllLeftTableTerms(selectTerm
0307:                                    .getLeftTableTerms());
0308:                            referredStatement.setTimeControl(timeControl);
0309:                            SearchResult referredResult = find(transaction,
0310:                                    referredStatement, null);
0311:                            if (referredResult.getResultSize() > 1) {
0312:                                throw new StoreException(
0313:                                        "object's last state was ambigous, results for object id '"
0314:                                                + currentId + "' was: "
0315:                                                + referredResult.getResult());
0316:                            } else if (referredResult.getResultSize() == 1) {
0317:                                // Set nonchanged attributes from this last state of object
0318:                                nonchangedAttributes = (Map) referredResult
0319:                                        .getResult().get(0);
0320:                                if (logger.isDebugEnabled())
0321:                                    logger
0322:                                            .debug("object nonchanged attributes: "
0323:                                                    + nonchangedAttributes);
0324:                            } else {
0325:                                // Object was not found, it most likely was deleted
0326:                                // so mark object as non-existent. This is a trick to
0327:                                // again save all attributes to database. For example,
0328:                                // when selecting an old instance, it is possible, the
0329:                                // instance is deleted in present time, so we must save
0330:                                // all attributes.
0331:                                context.getObjectTracker().makeUnexist(current);
0332:                            }
0333:                        } else {
0334:                            // Object does not exists, there are no nonchanged attributes
0335:                            logger
0336:                                    .debug("object did not exist, no nonchanged attributes.");
0337:                        }
0338:                        // }}}
0339:                        // {{{ Assembling changed attributes
0340:                        List attributeNames = classInfo.getAttributeNames();
0341:                        logger
0342:                                .debug("class tracker reported object to save has following attributes: "
0343:                                        + attributeNames
0344:                                        + ", it has id: "
0345:                                        + currentId
0346:                                        + ", object tracker says it exists: "
0347:                                        + context.getObjectTracker().exists(
0348:                                                current));
0349:                        for (int i = 0; i < attributeNames.size(); i++) {
0350:                            String attributeName = (String) attributeNames
0351:                                    .get(i);
0352:                            String attributeNameLowerCase = attributeName
0353:                                    .toLowerCase();
0354:                            // Do not handle special attributes
0355:                            if (("persistence_id"
0356:                                    .equals(attributeNameLowerCase))
0357:                                    || ("persistenceid"
0358:                                            .equals(attributeNameLowerCase))) {
0359:                                // We found a persistence id, fill it with id
0360:                                classInfo.setAttributeValue(current,
0361:                                        attributeName, new Long(currentId));
0362:                                continue;
0363:                            }
0364:                            // Skip reserved prefix'd attributes
0365:                            if (attributeNameLowerCase
0366:                                    .startsWith("persistence"))
0367:                                continue;
0368:                            // Handle non-special attributes
0369:                            Object attributeValue = classInfo
0370:                                    .getAttributeValue(current, attributeName);
0371:                            logger.debug("saving attribute: " + attributeName
0372:                                    + ", if changed.");
0373:                            if (context.getObjectTracker().hasChanged(
0374:                                    classInfo, current, attributeName,
0375:                                    nonchangedAttributes)) {
0376:                                // Current object's current attribute has changed,
0377:                                // so arrange it's save.
0378:                                // This is a switch for the type, this should be
0379:                                // replaced with something more adequate. For
0380:                                // example a type manager who will handle types
0381:                                // and is extensible.
0382:                                Class attributeType = classInfo
0383:                                        .getAttributeType(attributeName);
0384:                                switch (context.getClassTracker().getType(
0385:                                        attributeType)) {
0386:                                case ClassTracker.TYPE_PRIMITIVE:
0387:                                    // Primitive type, just add to attributes
0388:                                    changedAttributes.put(attributeName,
0389:                                            attributeValue);
0390:                                    // Set object tracker attribute
0391:                                    updatedAttributes.put(attributeName,
0392:                                            attributeValue);
0393:                                    break;
0394:                                case ClassTracker.TYPE_HANDLED:
0395:                                    logger.debug("changing handled attribute: "
0396:                                            + attributeName + ", type: "
0397:                                            + attributeType);
0398:                                    Object oldValue = context
0399:                                            .getObjectTracker()
0400:                                            .getAttributeValue(current,
0401:                                                    attributeName);
0402:                                    TypeHandler handler = context
0403:                                            .getTypeHandlerTracker()
0404:                                            .getHandler(attributeType);
0405:                                    Object newValue = handler.save(classInfo,
0406:                                            current, attributeName,
0407:                                            transaction, currentSerial,
0408:                                            oldValue, attributeValue,
0409:                                            waitingObjects, events,
0410:                                            changedAttributes,
0411:                                            nonchangedAttributes);
0412:                                    updatedAttributes.put(attributeName,
0413:                                            newValue);
0414:                                    if (newValue != null) {
0415:                                        savedHandledObjects.add(handler);
0416:                                        savedHandledObjects.add(newValue);
0417:                                    }
0418:                                    break;
0419:                                case ClassTracker.TYPE_OBJECT:
0420:                                    logger
0421:                                            .debug("attribute decided as custom object '"
0422:                                                    + attributeName
0423:                                                    + "', class was: "
0424:                                                    + classInfo
0425:                                                            .getAttributeType(attributeName));
0426:                                    // Object type
0427:                                    if (attributeValue == null) {
0428:                                        // Object type, but null
0429:                                        changedAttributes.put(attributeName,
0430:                                                null);
0431:                                        updatedAttributes.put(attributeName,
0432:                                                null);
0433:                                    } else {
0434:                                        if (!context.getObjectTracker().exists(
0435:                                                attributeValue)) {
0436:                                            // Object referred does not exists, so it will
0437:                                            // have to be created after we're done
0438:                                            waitingObjects
0439:                                                    .add(context
0440:                                                            .getObjectTracker()
0441:                                                            .getWrapper(
0442:                                                                    attributeValue));
0443:                                        }
0444:                                        context.getObjectTracker()
0445:                                                .registerObject(attributeValue,
0446:                                                        0);
0447:                                        long objectId = context
0448:                                                .getObjectTracker()
0449:                                                .getIdentifier(attributeValue);
0450:                                        // Got id, so give it to the man
0451:                                        changedAttributes.put(attributeName,
0452:                                                new Long(objectId));
0453:                                        // Set object tracker attribute
0454:                                        updatedAttributes.put(attributeName,
0455:                                                new Long(objectId));
0456:                                    }
0457:                                    break;
0458:                                default:
0459:                                    throw new StoreException(
0460:                                            "unknown type in attribute: "
0461:                                                    + attributeName
0462:                                                    + ", value was: "
0463:                                                    + attributeValue);
0464:                                }
0465:                            }
0466:                        } // End of iterating over attributes
0467:                        // }}}
0468:                        // {{{ Saving the object's changed attributes
0469:                        // Now do the database thing on the assembled changed 
0470:                        // attributes, since the waitingObjects list did not
0471:                        // change, meaning no new dependencies were discovered.
0472:                        // All changes are saved in multiple save/inserts according
0473:                        // to class structure.
0474:                        Iterator strictClassEntriesIterator = classInfo
0475:                                .getStrictClassEntries().iterator();
0476:                        while (strictClassEntriesIterator.hasNext()) {
0477:                            // Assemble changes for this strict class
0478:                            ClassEntry entry = (ClassEntry) strictClassEntriesIterator
0479:                                    .next();
0480:                            HashMap strictChanges = new HashMap();
0481:                            HashMap strictNonChanges = new HashMap();
0482:                            List strictAttributeNames = classInfo
0483:                                    .getStrictAttributeNames(entry);
0484:                            if (logger.isDebugEnabled())
0485:                                logger.debug("assembling changed for class: "
0486:                                        + entry + ", strict attributes: "
0487:                                        + strictAttributeNames);
0488:                            for (int i = 0; i < strictAttributeNames.size(); i++) {
0489:                                String attributeName = (String) strictAttributeNames
0490:                                        .get(i);
0491:                                Class attributeType = classInfo
0492:                                        .getAttributeType(attributeName);
0493:                                TypeHandler handler = context
0494:                                        .getTypeHandlerTracker().getHandler(
0495:                                                attributeType);
0496:                                if (handler == null) {
0497:                                    // No handler, this is a simple attribute
0498:                                    if (changedAttributes
0499:                                            .containsKey(attributeName))
0500:                                        strictChanges.put(attributeName,
0501:                                                changedAttributes
0502:                                                        .remove(attributeName));
0503:                                    if (nonchangedAttributes
0504:                                            .containsKey(attributeName))
0505:                                        strictNonChanges.put(attributeName,
0506:                                                nonchangedAttributes
0507:                                                        .remove(attributeName));
0508:                                } else {
0509:                                    // Got handler, then iterate on it's attributes
0510:                                    Map embeddedAttributes = handler
0511:                                            .getAttributeTypes(attributeName);
0512:                                    if (logger.isDebugEnabled())
0513:                                        logger
0514:                                                .debug("attribute: "
0515:                                                        + attributeName
0516:                                                        + ", was an embedded attribute, adding: "
0517:                                                        + embeddedAttributes);
0518:                                    Iterator embeddedAttributeNamesIterator = embeddedAttributes
0519:                                            .keySet().iterator();
0520:                                    while (embeddedAttributeNamesIterator
0521:                                            .hasNext()) {
0522:                                        attributeName = (String) embeddedAttributeNamesIterator
0523:                                                .next();
0524:                                        if (changedAttributes
0525:                                                .containsKey(attributeName))
0526:                                            strictChanges
0527:                                                    .put(
0528:                                                            attributeName,
0529:                                                            changedAttributes
0530:                                                                    .remove(attributeName));
0531:                                        if (nonchangedAttributes
0532:                                                .containsKey(attributeName))
0533:                                            strictNonChanges
0534:                                                    .put(
0535:                                                            attributeName,
0536:                                                            nonchangedAttributes
0537:                                                                    .remove(attributeName));
0538:                                    }
0539:                                }
0540:                            }
0541:                            // Make changes
0542:                            if (context.getObjectTracker().exists(current)) {
0543:                                // Save
0544:                                logger
0545:                                        .debug("changing object with following attributes: "
0546:                                                + strictChanges
0547:                                                + ", not changed: "
0548:                                                + strictNonChanges);
0549:                                if (strictChanges.size() > 0) {
0550:                                    // First set enddate on used entry
0551:                                    HashMap removeChanges = new HashMap();
0552:                                    HashMap keys = new HashMap();
0553:                                    keys.put("persistence_id", new Long(
0554:                                            currentId));
0555:                                    keys.put("persistence_end", new Long(
0556:                                            DateSerialUtil.getMaxSerial()));
0557:                                    keys.put("persistence_txend", new Long(
0558:                                            DateSerialUtil.getMaxSerial()));
0559:                                    removeChanges.put("persistence_txend",
0560:                                            currentSerial);
0561:                                    removeChanges.put("persistence_txendid",
0562:                                            currentTxSerial);
0563:                                    context.getDatabase().save(transaction,
0564:                                            classInfo.getTableName(entry),
0565:                                            keys, removeChanges);
0566:                                    transaction.addRemoveTable(classInfo
0567:                                            .getTableName(entry));
0568:                                    // Create new entry
0569:                                    strictNonChanges.putAll(strictChanges);
0570:                                    strictNonChanges.put("persistence_id",
0571:                                            new Long(currentId));
0572:                                    strictNonChanges.put("persistence_start",
0573:                                            new Long(DateSerialUtil
0574:                                                    .getMaxSerial()));
0575:                                    strictNonChanges.put("persistence_end",
0576:                                            new Long(DateSerialUtil
0577:                                                    .getMaxSerial()));
0578:                                    strictNonChanges.put("persistence_txendid",
0579:                                            new Long(0));
0580:                                    strictNonChanges.put("persistence_txstart",
0581:                                            currentSerial);
0582:                                    strictNonChanges.put(
0583:                                            "persistence_txstartid",
0584:                                            currentTxSerial);
0585:                                    strictNonChanges.put("persistence_txend",
0586:                                            new Long(DateSerialUtil
0587:                                                    .getMaxSerial()));
0588:                                    context.getDatabase().insert(transaction,
0589:                                            classInfo.getTableName(entry),
0590:                                            strictNonChanges);
0591:                                    // Add to modified tables list
0592:                                    transaction.addSaveTable(classInfo
0593:                                            .getTableName(entry));
0594:                                }
0595:                            } else {
0596:                                // Insert
0597:                                logger
0598:                                        .debug("inserting object with following attributes: "
0599:                                                + strictChanges);
0600:                                strictChanges.put("persistence_id", new Long(
0601:                                        currentId));
0602:                                strictChanges
0603:                                        .put("persistence_start", new Long(
0604:                                                DateSerialUtil.getMaxSerial()));
0605:                                strictChanges.put("persistence_end", new Long(
0606:                                        DateSerialUtil.getMaxSerial()));
0607:                                strictChanges.put("persistence_txendid",
0608:                                        new Long(0));
0609:                                strictChanges.put("persistence_txstart",
0610:                                        currentSerial);
0611:                                strictChanges.put("persistence_txstartid",
0612:                                        currentTxSerial);
0613:                                strictChanges
0614:                                        .put("persistence_txend", new Long(
0615:                                                DateSerialUtil.getMaxSerial()));
0616:                                context.getDatabase().insert(transaction,
0617:                                        classInfo.getTableName(entry),
0618:                                        strictChanges);
0619:                                // Add to modified tables list
0620:                                transaction.addSaveTable(classInfo
0621:                                        .getTableName(entry));
0622:                            }
0623:                        }
0624:                        if (changedAttributes.size() != 0)
0625:                            logger
0626:                                    .warn("there are attributes that do not belong in any superclass of object to be saved, classinfo: "
0627:                                            + classInfo
0628:                                            + ", attributes: "
0629:                                            + changedAttributes);
0630:                        logger.debug("saving object with id: " + currentId
0631:                                + " updating meta-data.");
0632:                        // Remove object from waiting list
0633:                        waitingObjects.remove(currentWrapper);
0634:                        // Notify event listeners
0635:                        if (context.getObjectTracker().exists(current)) {
0636:                            // Modify
0637:                            events.add(new ModifyObjectEvent(current));
0638:                        } else {
0639:                            // Create
0640:                            events.add(new CreateObjectEvent(current));
0641:                        }
0642:                        // Update object tracker
0643:                        context.getObjectTracker().updateObject(current,
0644:                                updatedAttributes);
0645:                        context.getObjectTracker().makeExist(current); // Exists for this transaction
0646:                        // }}}
0647:                        logger.debug("saving object with id: " + currentId
0648:                                + " finished.");
0649:                    }
0650:                    // {{{ Go through handled objects, and notify that save ended
0651:                    for (int i = 0; i < savedHandledObjects.size();) {
0652:                        TypeHandler handler = (TypeHandler) savedHandledObjects
0653:                                .get(i++);
0654:                        Object value = (Object) savedHandledObjects.get(i++);
0655:                        handler.postSave(value);
0656:                    }
0657:                    // }}}
0658:                    // {{{ Notify dispatcher of events occured during save
0659:                    for (int i = 0; i < events.size(); i++)
0660:                        context.getEventDispatcher().notify(
0661:                                (PersistenceEvent) events.get(i));
0662:                    // }}}
0663:                    // "Happy, happy, joy, joy". Object saved.
0664:                } catch (StoreException e) {
0665:                    transaction.rollback();
0666:                    logger.error("throwing store exception", e);
0667:                    throw e;
0668:                } catch (Throwable e) {
0669:                    transaction.rollback();
0670:                    logger.error("throwing unexpected exception", e);
0671:                    throw new StoreException("unexpected exception", e);
0672:                }
0673:                transaction.commit();
0674:                /// }}}
0675:            }
0676:
0677:            /**
0678:             * Remove the object given. If the object is not stored yet, no
0679:             * operation will take place.
0680:             * @param obj The object to remove.
0681:             * @throws StoreException If remove is not successfull.
0682:             */
0683:            public void remove(Object obj) {
0684:                // {{{ Remove object
0685:                // Transaction
0686:                Transaction transaction = context.getTransactionTracker()
0687:                        .getTransaction(TransactionTracker.TX_REQUIRED);
0688:                transaction.begin();
0689:                transaction.setLastOperation("removing object: " + obj);
0690:                try {
0691:                    // Check id. If id is not given, this object does not exists,
0692:                    // do nothing.
0693:                    long id = context.getObjectTracker().getIdentifier(obj);
0694:                    if (id == 0)
0695:                        return;
0696:                    Long currentSerial = context.getNodeManager()
0697:                            .getNextSerial();
0698:                    Long currentTxSerial = transaction.getSerial(currentSerial);
0699:                    // Lock object
0700:                    context.getLockTracker().lock(obj);
0701:                    transaction.addRemovedObject(context.getObjectTracker()
0702:                            .getWrapper(obj));
0703:                    // Get class info and assemble attributes identifying
0704:                    // the object
0705:                    ClassInfo classInfo = context.getClassTracker()
0706:                            .getClassInfo(obj.getClass(), obj);
0707:                    Map removeChanges = new HashMap();
0708:                    logger.debug("remove called, using serial: "
0709:                            + currentSerial);
0710:                    removeChanges.put("persistence_txend", currentSerial);
0711:                    removeChanges.put("persistence_txendid", currentTxSerial);
0712:                    Map keys = new HashMap();
0713:                    keys.put("persistence_id", new Long(id));
0714:                    keys.put("persistence_end", new Long(DateSerialUtil
0715:                            .getMaxSerial()));
0716:                    keys.put("persistence_txend", new Long(DateSerialUtil
0717:                            .getMaxSerial()));
0718:                    // Execute remove on class and all superclasses, et voila'
0719:                    for (int i = 0; i < classInfo.getStrictClassEntries()
0720:                            .size(); i++) {
0721:                        ClassEntry entry = (ClassEntry) classInfo
0722:                                .getStrictClassEntries().get(i);
0723:                        context.getDatabase().save(transaction,
0724:                                classInfo.getTableName(entry), keys,
0725:                                removeChanges);
0726:                        // Add changed table
0727:                        transaction.addRemoveTable(classInfo
0728:                                .getTableName(entry));
0729:                    }
0730:                    // Notify event listeners
0731:                    context.getEventDispatcher().notify(
0732:                            new DeleteObjectEvent(obj));
0733:                } catch (StoreException e) {
0734:                    transaction.rollback();
0735:                    logger.error("throwing store exception", e);
0736:                    throw e;
0737:                } catch (Throwable e) {
0738:                    transaction.rollback();
0739:                    logger.error("throwing unexpected exception", e);
0740:                    throw new StoreException("unexpected exception", e);
0741:                }
0742:                transaction.commit();
0743:                /// }}}
0744:            }
0745:
0746:            /**
0747:             * Query an object from the datastore. The List returned is 
0748:             * a lazy list, the implementation tries to limit the communication
0749:             * with the database layer, as much as possible and pratical. Only
0750:             * parts of the list will be loaded when an item is referenced, not
0751:             * the whole list. Some features of the query language:<br>
0752:             * <pre>find book where book.name='Snow Crash'</pre>
0753:             * The statement always starts with the keyword 'find', and all
0754:             * keywords and parts of the statement not between apostrophs are
0755:             * case in-sensitive.<br>
0756:             * The second word of the statement determines
0757:             * the class you are trying to find. You can abbreviate the classname
0758:             * (strip the package) if it is unique, but you can use the full
0759:             * name (com.acme.book) if you wish, but then you MUST provide an
0760:             * alias name (see below)<br>
0761:             * The following parts are all optional. First, there can be
0762:             * a select statement, to specify which objects to select which are
0763:             * instances of given class. If you want to have a where part, the
0764:             * third word should be 'where'. After it there should be an
0765:             * expression almost as in SQL. Parts of the expression can be:<br>
0766:             * <ul>
0767:             *    <li><strong>Member attributes of classes.</strong> The class given previously
0768:             *    (the target of statement) is always available as declared. If
0769:             *    you wish to use other classes, they can be referenced by
0770:             *    class (abbreviated or fully declared). For example
0771:             *    <pre>find book where book.author=author and author.birthdate=1959</pre>
0772:             *    Of course Book, and Author classes must exits in the store with
0773:             *    given attributes.
0774:             *    Note also, that the above statement can be simply written:
0775:             *    <pre>find book where book.author.birthdate=1959</pre>
0776:             *    You can name the classes you are referencing for later
0777:             *    use (handy if you must "join" the class with itself):
0778:             *    <pre>find book where book.author=otherbook(book).author and otherbook.name='Snow Crash'</pre>
0779:             *    Here, the "otherbook" does not refer to a class, it is only another
0780:             *    name for the class book, and means, that is should be not the same
0781:             *    instance as the one referenced with "book".
0782:             *    </li>
0783:             *    <li><strong>"Now" constant</strong>: For easy use the current date/time
0784:             *    is represented with the special word 'now'. So you can write:
0785:             *    <pre>find movie where movie.startdate &gt; now</pre>
0786:             *    This also means, that attributes named 'now' must be escaped (prefixed
0787:             *    with it's table name).
0788:             *    <li><strong>Constants</strong>: Numbers and strings, see examples above. 
0789:             *    Dates and objects can be given by using the question mark (?), and adding the object
0790:             *    as parameters (same as in jdbc).</li>
0791:             *    <li><strong>The operators</strong>: &lt;, &gt;, =, !=, like, is null, not null, is not null.
0792:             *    Note however, that not all database backends are required to support
0793:             *    all of these. If they are not supported, an exception will be thrown.</li>
0794:             *    <li><strong>Logical operators</strong>: or, and, not. See examples above.</li>
0795:             *    <li><strong>Grouping with parenthesis.</strong> As usual in expressions,
0796:             *    you can use grouping:
0797:             *    <pre>find book where ((book.author.firstname='Neal') and (book.author.lastname='Stephenson'))</pre>
0798:             *    </li>
0799:             *    <li><strong>Special container operator</strong>: contains. 
0800:             *    These are used in conjunction with container types such as Map and List:
0801:             *    <pre>find book where book.genres contains genre and genre.name='postcyberpunk'</pre>
0802:             *    <pre>find author where author.books contains book and book.name='Snow Crash'</pre>
0803:             *    A container operator can not be negated, an exception will be thrown,
0804:             *    if the expression would try to do that.
0805:             *    </li>
0806:             *    <li><strong>Special map operator</strong>: [, ]. These are used when
0807:             *    referencing a Map. Note also, that [] can only contain strings.
0808:             *    <pre>find book where book.metadata['author']=author and author.name='Neal Stephenson</pre>
0809:             *    However, if after a map operator an attribute is referenced, 
0810:             *    there is a mandatory class specifier:
0811:             *    <pre>find book where book.metadata['author'](author).name='Neal Stephenson</pre>
0812:             *    </li>
0813:             * </ul><br>
0814:             * You can also sort the result list with the 'order by' command:
0815:             * <pre>find book order by book.name asc</pre>
0816:             * The order by command takes attributes as aguments. 
0817:             * You can give more than one attribute separated by commas.
0818:             * Also you can append 'asc' (ascending) or 'desc' (descending) to
0819:             * mark the direction of sort.
0820:             * <pre>find book order by book.author.name asc, book.name desc</pre>
0821:             * Note, that method will silently return an empty list, if the
0822:             * specified table or one specified in where clause does not exist.<br>
0823:             * For more detailed information, check the documentation.
0824:             * @param statement The query statement to select.
0825:             */
0826:            public List find(String statement) {
0827:                return find(statement, null, null, null);
0828:            }
0829:
0830:            /**
0831:             * Same as <code>find(statement)</code>. When a statement contains
0832:             * the question mark (?), the object which should be in the place of
0833:             * the mark should be given as parameters (parameters are usually of
0834:             * Date, or custom classes).
0835:             * @param statement The query statement to execute.
0836:             * @param parameters The parameters.
0837:             */
0838:            public List find(String statement, Object[] parameters) {
0839:                return find(statement, parameters, null, null);
0840:            }
0841:
0842:            /**
0843:             * This method in addition to all usual parameters can define the
0844:             * exact time of the query with the timeControl parameter. If the
0845:             * query is a historical query, this control will be overridden.
0846:             * @param statement The query statement to execute.
0847:             * @param parameters The parameters.
0848:             * @param timeControl The exact default time of the query.
0849:             */
0850:            List find(String statement, Object[] parameters,
0851:                    TimeControl timeControl, Map unmarshalledObjects) {
0852:                // {{{ Parse statement, and create result list
0853:                // Convert object parameters. If they contain
0854:                // objects, substitute with object id
0855:                Transaction transaction = context.getTransactionTracker()
0856:                        .getTransaction(TransactionTracker.TX_REQUIRED);
0857:                transaction.begin();
0858:                transaction.setLastOperation(statement);
0859:                try {
0860:                    Object[] realParameters = null;
0861:                    if (parameters != null) {
0862:                        realParameters = new Object[parameters.length];
0863:                        for (int i = 0; i < parameters.length; i++) {
0864:                            if (parameters[i] == null) {
0865:                                // Handle null parameters
0866:                                realParameters[i] = null;
0867:                            } else {
0868:                                // If parameter is not null, translate object parameters
0869:                                // to persistent id, leave others
0870:                                int type = context.getClassTracker().getType(
0871:                                        parameters[i].getClass());
0872:                                if (type == ClassTracker.TYPE_RESERVED)
0873:                                    throw new StoreException(
0874:                                            "parameter at position: "
0875:                                                    + i
0876:                                                    + ", value: "
0877:                                                    + parameters[i]
0878:                                                    + " is of unsupported type.");
0879:                                if ((type == ClassTracker.TYPE_OBJECT)
0880:                                        && (!(parameters[i] instanceof  Collection))) {
0881:                                    // If object has no id, that's not a problem. It will
0882:                                    // receive an id of 0, and no object should match
0883:                                    // that id anyway.
0884:                                    realParameters[i] = new Identifier(
0885:                                            new Long(context.getObjectTracker()
0886:                                                    .getIdentifier(
0887:                                                            parameters[i])));
0888:                                } else if (parameters[i] instanceof  Collection) {
0889:                                    // Check, whether the collection's items are objects,
0890:                                    // in which case translate them to their ids
0891:                                    Vector result = new Vector();
0892:                                    Iterator itemIterator = ((Collection) parameters[i])
0893:                                            .iterator();
0894:                                    while (itemIterator.hasNext()) {
0895:                                        Object item = itemIterator.next();
0896:                                        if (context.getClassTracker().getType(
0897:                                                item.getClass()) == ClassTracker.TYPE_OBJECT)
0898:                                            result.add(new Long(context
0899:                                                    .getObjectTracker()
0900:                                                    .getIdentifier(item)));
0901:                                        else
0902:                                            result.add(item);
0903:                                    }
0904:                                    realParameters[i] = result;
0905:                                } else {
0906:                                    realParameters[i] = parameters[i];
0907:                                }
0908:                            }
0909:                            if (logger.isDebugEnabled())
0910:                                logger.debug("parameter: " + parameters[i]
0911:                                        + " -> real parameter #" + i + ":"
0912:                                        + realParameters[i]);
0913:                        }
0914:                    }
0915:                    // Process it, get expression
0916:                    QueryStatementList stmts = null;
0917:                    try {
0918:                        // Only apply in-transaction search conditions if there is a transaction
0919:                        // and something changed during transaction. Only calculate if
0920:                        // no 'default default' is given.
0921:                        HashSet modifiedTables = new HashSet();
0922:                        modifiedTables.addAll(transaction.getSaveTables());
0923:                        modifiedTables.addAll(transaction.getRemoveTables());
0924:                        if (timeControl == null) {
0925:                            Long serial = context.getNodeManager()
0926:                                    .getNextSerial();
0927:                            Long txSerial = transaction.getSerial(serial);
0928:                            timeControl = new TimeControl(serial, txSerial,
0929:                                    modifiedTables.size() > 0);
0930:                        }
0931:                        // Parse statement
0932:                        if (logger.isDebugEnabled())
0933:                            logger.debug("executing parser, serial: "
0934:                                    + timeControl.getSerial() + ", tx serial: "
0935:                                    + timeControl.getTxSerial());
0936:                        stmts = Parser.parse(statement, new WhereResolver(
0937:                                context), realParameters, timeControl,
0938:                                modifiedTables);
0939:                        // Wait now for all commits() before the given serial to finish.
0940:                        // If this would not be the case, the lazy list might not
0941:                        // contain the data from previously initiated commits. Which
0942:                        // would mean, once those finished, the lazy list would change.
0943:                        context.getNodeManager().waitForQuery(
0944:                                timeControl.getSerial());
0945:                    } catch (ParserException e) {
0946:                        if (e.getCode() == ParserException.ABORT) {
0947:                            logger
0948:                                    .error("aborting query, because of parser exception.");
0949:                            throw new StoreException(e.getMessage(), e);
0950:                        } else {
0951:                            logger
0952:                                    .info("returning empty result list because of non-fatal symbol error. Parser said: "
0953:                                            + e.getMessage()
0954:                                            + ", statement was: " + statement);
0955:                            return new LinkedList(); // Return empty list on non-fatal symbol errors
0956:                        }
0957:                    } catch (StoreException e) {
0958:                        throw e;
0959:                    } catch (Exception e) {
0960:                        throw new StoreException(
0961:                                "unknown exception while select", e);
0962:                    }
0963:                    // Return list
0964:                    return new LazyList(context, stmts, unmarshalledObjects);
0965:                } catch (StoreException e) {
0966:                    transaction.markRollbackOnly();
0967:                    throw e;
0968:                } catch (Throwable e) {
0969:                    transaction.markRollbackOnly();
0970:                    throw new StoreException("unexpected exception", e);
0971:                } finally {
0972:                    transaction.commit();
0973:                }
0974:                // }}}
0975:            }
0976:
0977:            /**
0978:             * Same as <code>find(statement,parameters)</code>, but the result should be
0979:             * a single object.
0980:             * @param statement The query statement to execute.
0981:             * @param parameters The parameters to the statement.
0982:             * @return The object selected, or null if no such object exists. If
0983:             * the result contains more objects, an arbitrary one is selected.
0984:             */
0985:            public Object findSingle(String statement, Object[] parameters) {
0986:                // {{{ Call normal find and evaluate result
0987:                Iterator iterator = find(statement, parameters).iterator();
0988:                if (iterator.hasNext())
0989:                    return iterator.next();
0990:                return null;
0991:                // }}}
0992:            }
0993:
0994:            /**
0995:             * Same as <code>find(statement)</code>, but the result should be
0996:             * a single object.
0997:             * @param statement The query statement to execute.
0998:             * @return The object selected, or null if no such object exists. If
0999:             * the result contains more objects, an arbitrary one is selected.
1000:             */
1001:            public Object findSingle(String statement) {
1002:                return findSingle(statement, null);
1003:            }
1004:
1005:            /**
1006:             * Internal raw loading. All finder methods sooner or later call into
1007:             * this method to get real results in form of attribute name-value maps.
1008:             */
1009:            SearchResult find(Transaction transaction, QueryStatement stmt,
1010:                    Limits limits) {
1011:                // {{{ Do physical query
1012:                // First, check if statement is visible from this transaction
1013:                TimeControl timeControl = stmt.getTimeControl();
1014:                if ((timeControl.isApplyTransaction())
1015:                        && (timeControl.getTxSerial() != null)
1016:                        && (!timeControl.getTxSerial().equals(
1017:                                transaction.getSerial()))
1018:                        && (context.getTransactionTracker()
1019:                                .hasOpenTransaction(timeControl.getTxSerial())))
1020:                    throw new StoreException(
1021:                            "tried to do a query which was outside it's transaction '"
1022:                                    + timeControl.getTxSerial()
1023:                                    + "', which was still open. Current tx was: "
1024:                                    + transaction.getSerial());
1025:                // Get resultset from cache or database
1026:                SearchResult result = context.getCache().getEntry(stmt, limits);
1027:                if (result == null) {
1028:                    result = context.getDatabase().search(transaction, stmt,
1029:                            limits);
1030:                    context.getCache().addEntry(stmt, limits, result);
1031:                }
1032:                return result;
1033:                // }}}
1034:            }
1035:
1036:            /**
1037:             * Unmarshall an object. This means create an object of given class
1038:             * and set attributes from a given map of attributes. All referred
1039:             * objects are assumed to be in the already allocated list, indexed
1040:             * by object id.
1041:             * @param classInfo The class info of the object that needs to be instantiated.
1042:             * @param marshalledValues The attributes values.
1043:             * @param unmarshalledObjects The already unmarshalled objects.
1044:             */
1045:            private Object unmarshallObject(ClassInfo classInfo,
1046:                    Map marshalledValues, Map unmarshalledObjects,
1047:                    Map missingAttributes, QueryStatement stmt)
1048:                    throws InstantiationException, IllegalAccessException {
1049:                // {{{ Umarshall object
1050:                Object obj = classInfo.newInstance(marshalledValues);
1051:                if (obj == null)
1052:                    return null;
1053:                // Important! Register object into tracker!
1054:                // Note: all attributes are updated into the object tracker.
1055:                // Previously this was not done, because object's had a shared
1056:                // state (instances of the same database row), but now no shared
1057:                // state exists (at least, not with attributes).
1058:                context.getObjectTracker().registerObject(
1059:                        obj,
1060:                        ((Long) marshalledValues.get("persistence_id"))
1061:                                .longValue());
1062:                context.getObjectTracker().updateObject(obj, marshalledValues);
1063:                PersistenceMetaData metaData = context.getObjectTracker()
1064:                        .getMetaData(obj);
1065:                metaData.setPersistenceStart(((Long) marshalledValues
1066:                        .get("persistence_start")));
1067:                metaData.setPersistenceEnd(((Long) marshalledValues
1068:                        .get("persistence_end")));
1069:                metaData.setQuerySerial(stmt.getTimeControl().getSerial());
1070:                unmarshalledObjects.put(marshalledValues.get("persistence_id"),
1071:                        obj);
1072:                // Set properties in obj, go through object attributes
1073:                // (except is the object is of primitive type)
1074:                List attributeNames = classInfo.getAttributeNames();
1075:                for (int o = 0; (o < attributeNames.size())
1076:                        && (!classInfo.getSourceEntry().isPrimitive()); o++) {
1077:                    String attributeName = attributeNames.get(o).toString();
1078:                    // Handle peristence_id specially. If this is an attribute
1079:                    // named something like persistenceId, then fill in the id.
1080:                    if (("persistence_id".equalsIgnoreCase(attributeName))
1081:                            || ("persistenceid".equalsIgnoreCase(attributeName))) {
1082:                        // Fill attribute with persistence id
1083:                        classInfo.setAttributeValue(obj, attributeName,
1084:                                marshalledValues.get("persistence_id"));
1085:                        continue;
1086:                    }
1087:                    // Handle other (normal) attributes
1088:                    Object attributeValue = marshalledValues.get(attributeName
1089:                            .toLowerCase());
1090:                    if (logger.isDebugEnabled())
1091:                        logger.debug("setting object property: "
1092:                                + attributeName + ", value: " + attributeValue);
1093:                    Class attributeClass = classInfo
1094:                            .getAttributeType(attributeName);
1095:                    if (attributeClass == null) {
1096:                        logger.error("object property '" + attributeName
1097:                                + "' cannot set, object has no such property.");
1098:                        continue;
1099:                    }
1100:                    switch (context.getClassTracker().getType(attributeClass)) {
1101:                    case ClassTracker.TYPE_PRIMITIVE:
1102:                        classInfo.setAttributeValue(obj, attributeName,
1103:                                attributeValue);
1104:                        break;
1105:                    case ClassTracker.TYPE_HANDLED:
1106:                        TypeHandler handler = context.getTypeHandlerTracker()
1107:                                .getHandler(attributeClass);
1108:                        Object value = handler.unmarshallType(classInfo, obj,
1109:                                attributeName, marshalledValues, stmt
1110:                                        .getTimeControl());
1111:                        classInfo.setAttributeValue(obj, attributeName, value);
1112:                        context.getObjectTracker().updateAttribute(obj,
1113:                                attributeName, value);
1114:                        break;
1115:                    case ClassTracker.TYPE_OBJECT:
1116:                        // Handle null
1117:                        if (attributeValue == null) {
1118:                            classInfo.setAttributeValue(obj, attributeName,
1119:                                    null);
1120:                            break;
1121:                        }
1122:                        // Get object from list
1123:                        Object relatedObj = unmarshalledObjects
1124:                                .get(attributeValue);
1125:                        classInfo.setAttributeValue(obj, attributeName,
1126:                                relatedObj);
1127:                        if (relatedObj == null) {
1128:                            if (logger.isDebugEnabled())
1129:                                logger
1130:                                        .debug("referred object of id: "
1131:                                                + attributeValue
1132:                                                + " not found, currently unmarshalled objects: "
1133:                                                + unmarshalledObjects);
1134:                            // Remember with ids entry. An entry holds all necessary
1135:                            // information for a single attribute to reconstruct
1136:                            // it's objects for all referrers:
1137:                            // - classinfo: of attribute declared type (for select)
1138:                            // - ids: all ids of referred objects
1139:                            // - objects: indexed by referred object id, hold a set
1140:                            //   of objects which need the specified referred object
1141:                            IdsEntry idsEntry = (IdsEntry) missingAttributes
1142:                                    .get(attributeName);
1143:                            if (idsEntry == null) {
1144:                                idsEntry = new IdsEntry(new HashMap(),
1145:                                        new HashSet(), classInfo);
1146:                                missingAttributes.put(attributeName, idsEntry);
1147:                            }
1148:                            idsEntry.ids.add(attributeValue);
1149:                            List objectsSet = (List) idsEntry.objects
1150:                                    .get(attributeValue);
1151:                            if (objectsSet == null) {
1152:                                objectsSet = new Vector();
1153:                                idsEntry.objects
1154:                                        .put(attributeValue, objectsSet);
1155:                            }
1156:                            objectsSet.add(obj);
1157:                        }
1158:                        break;
1159:                    default:
1160:                        throw new StoreException("attribute: " + attributeName
1161:                                + "'s type was not valid.");
1162:                    }
1163:                } // Iteration over attributes
1164:                return obj;
1165:                // }}}
1166:            }
1167:
1168:            /**
1169:             * Internal loading.
1170:             */
1171:            SearchResult find(QueryStatement rawStmt, Limits limits,
1172:                    Map unmarshalledObjects) {
1173:                // {{{ Internal loading code
1174:                if (unmarshalledObjects == null)
1175:                    unmarshalledObjects = new HashMap();
1176:                Transaction transaction = context.getTransactionTracker()
1177:                        .getTransaction(TransactionTracker.TX_REQUIRED);
1178:                transaction.begin();
1179:                logger.debug("called store internal find.");
1180:                try {
1181:                    // If the query is a view query, then return raw map format.
1182:                    // Else get the main term.
1183:                    if (rawStmt.getMode() == QueryStatement.MODE_VIEW)
1184:                        return find(transaction, rawStmt, limits);
1185:                    TableTerm mainTerm = (TableTerm) rawStmt.getSelectTerms()
1186:                            .get(0);
1187:                    // Modify the query statement, so the statement should return
1188:                    // the object's table in question (so the object instance can be
1189:                    // created)
1190:                    ClassInfo classInfo = context.getClassTracker()
1191:                            .getTableClassInfo(mainTerm.getTableName());
1192:                    if (classInfo == null)
1193:                        throw new StoreException(
1194:                                "no class found for table name: "
1195:                                        + mainTerm.getTableName());
1196:                    QueryStatement stmt = new QueryStatement(rawStmt);
1197:                    if (logger.isDebugEnabled())
1198:                        logger.debug("main term is: " + mainTerm
1199:                                + ", left terms: "
1200:                                + mainTerm.getLeftTableTerms());
1201:                    if (mainTerm.getLeftTableTerms().size() > 0) {
1202:                        // Modify main term to include object table information for
1203:                        // un-marshalling
1204:                        Vector mainTerms = new Vector();
1205:                        TableTerm mainTermCopy = new TableTerm(mainTerm
1206:                                .getTableName(), mainTerm.getAlias(),
1207:                                new Vector(mainTerm.getLeftTableTerms()));
1208:                        mainTermCopy.getLeftTableTerms().add(
1209:                                new TableTerm("persistence_object_ids", null));
1210:                        mainTerms.add(mainTermCopy);
1211:                        mainTerms.addAll(stmt.getSelectTerms().subList(1,
1212:                                stmt.getSelectTerms().size()));
1213:                        stmt.setSelectTerms(mainTerms);
1214:                        stmt.setStaticRepresentation(stmt
1215:                                .getStaticRepresentation()
1216:                                + "-ids");
1217:                    }
1218:                    // Run the real database search
1219:                    logger.debug("find running real select statement.");
1220:                    SearchResult rawResult = find(transaction, stmt, limits);
1221:                    // Take the raw data and umarshall them into objects
1222:                    logger.debug("find unmarshalling objects.");
1223:                    HashMap missingAttributes = new HashMap();
1224:                    SearchResult cookedResult = new SearchResult();
1225:                    cookedResult.setResultSize(rawResult.getResultSize());
1226:                    Vector cookedResultList = new Vector();
1227:                    cookedResult.setResult(cookedResultList);
1228:                    for (int i = 0; i < rawResult.getResult().size(); i++) {
1229:                        // Get values
1230:                        Map marshalledValues = (Map) rawResult.getResult().get(
1231:                                i);
1232:                        // If object already unmarshalled, then get from list,
1233:                        // else instantiate
1234:                        Object obj = unmarshalledObjects.get(marshalledValues
1235:                                .get("persistence_id"));
1236:                        if (obj == null) {
1237:                            if (logger.isDebugEnabled())
1238:                                logger.debug("got marshalled values: "
1239:                                        + marshalledValues);
1240:                            // Instantiate and unmarshall object
1241:                            ClassInfo localClassInfo = classInfo;
1242:                            if (mainTerm.getLeftTableTerms().size() > 0) {
1243:                                // If there were left join tables, try to get the
1244:                                // correct class. This means, that the object
1245:                                // can be the subclass of queried class, and not
1246:                                // exactly that. We can determine the exact class
1247:                                // from the table name.
1248:                                String objectTable = (String) marshalledValues
1249:                                        .get("object_table");
1250:                                localClassInfo = context.getClassTracker()
1251:                                        .getTableClassInfo(objectTable);
1252:                            }
1253:                            // Unmarshall, with the exact class info given
1254:                            obj = unmarshallObject(localClassInfo,
1255:                                    marshalledValues, unmarshalledObjects,
1256:                                    missingAttributes, rawStmt);
1257:                        }
1258:                        // Add object to result list. The 'obj' is an umarshalled full
1259:                        // object, which is gethered from the main table of the query.
1260:                        // How ever, if there are other referenced attributes the
1261:                        // query should return, then we do the whole thing into a Map.
1262:                        // The object itself will have the key 'object' in this case.
1263:                        if (rawStmt.getSelectTerms().size() > 1) {
1264:                            // There are other attributes the caller wants, so
1265:                            // do the whole thing into a Map. Insert all wanted attributes,
1266:                            // and the unmarshalled main object too.
1267:                            HashMap resultObj = new HashMap();
1268:                            for (int o = 1; o < rawStmt.getSelectTerms().size(); o++) {
1269:                                ReferenceTerm refTerm = (ReferenceTerm) rawStmt
1270:                                        .getSelectTerms().get(o);
1271:                                resultObj.put(refTerm.getColumnFinalName(),
1272:                                        marshalledValues.get(refTerm
1273:                                                .getColumnFinalName()
1274:                                                .toLowerCase()));
1275:                            }
1276:                            resultObj.put("object", obj);
1277:                            cookedResultList.add(resultObj);
1278:                        } else {
1279:                            // There was just the object, so insert it into the result
1280:                            // list, just in itself.
1281:                            cookedResultList.add(obj);
1282:                        }
1283:                    }
1284:                    // Load all referred objects for all classes which were unmarshalled,
1285:                    // the missing list was assembled in the unmarshall code.
1286:                    Iterator missingAttributesIterator = missingAttributes
1287:                            .entrySet().iterator();
1288:                    while (missingAttributesIterator.hasNext()) {
1289:                        // Get all necessary meta-data
1290:                        Map.Entry entry = (Map.Entry) missingAttributesIterator
1291:                                .next();
1292:                        String attributeName = entry.getKey().toString();
1293:                        IdsEntry idsEntry = (IdsEntry) entry.getValue();
1294:                        Class selectClass = idsEntry.classInfo
1295:                                .getAttributeType(attributeName);
1296:                        // We got the class of the object, so we select all objects to
1297:                        // this attribute into a map keyed with the persistence id.
1298:                        HashMap referredObjects = new HashMap();
1299:                        if (logger.isDebugEnabled())
1300:                            logger.debug("getting member attribute: "
1301:                                    + selectClass + ", for ids: "
1302:                                    + idsEntry.ids);
1303:                        List referredObjectList = find(
1304:                                "find member(" + selectClass.getName()
1305:                                        + ") where member in ?",
1306:                                new Object[] { idsEntry.ids }, null,
1307:                                unmarshalledObjects);
1308:                        for (int i = 0; i < referredObjectList.size(); i++) {
1309:                            Object referredObject = referredObjectList.get(i);
1310:                            referredObjects.put(new Long(context
1311:                                    .getObjectTracker().getIdentifier(
1312:                                            referredObject)), referredObject);
1313:                        }
1314:                        // Now fill in this attribute with the ready referred objects
1315:                        Iterator objectEntryIterator = idsEntry.objects
1316:                                .entrySet().iterator();
1317:                        while (objectEntryIterator.hasNext()) {
1318:                            Map.Entry objectEntry = (Map.Entry) objectEntryIterator
1319:                                    .next();
1320:                            Object referredObject = referredObjects
1321:                                    .get(objectEntry.getKey());
1322:                            // Now set to all referring objects
1323:                            Iterator objectIterator = ((List) objectEntry
1324:                                    .getValue()).iterator();
1325:                            while (objectIterator.hasNext()) {
1326:                                Object referrerObject = objectIterator.next();
1327:                                ClassInfo referrerClassInfo = context
1328:                                        .getClassTracker().getClassInfo(
1329:                                                referrerObject.getClass(),
1330:                                                referrerObject);
1331:                                referrerClassInfo.setAttributeValue(
1332:                                        referrerObject, attributeName,
1333:                                        referredObject);
1334:                            }
1335:                        }
1336:                    }
1337:                    // Return result
1338:                    logger.debug("find returning result list");
1339:                    return cookedResult;
1340:                } catch (StoreException e) {
1341:                    transaction.markRollbackOnly();
1342:                    logger.error("throwing store exception", e);
1343:                    throw e;
1344:                } catch (Exception e) {
1345:                    transaction.markRollbackOnly();
1346:                    logger.error("throwing unexpected exception", e);
1347:                    throw new StoreException("unexpected exception", e);
1348:                } finally {
1349:                    transaction.commit();
1350:                }
1351:                // }}}
1352:            }
1353:
1354:            /**
1355:             * Unlock all objects in transaction.
1356:             */
1357:            private void unlockObjects(Transaction transaction) {
1358:                // {{{ Unlock all objects in transaction
1359:                List removedObjects = transaction.getRemovedObjects();
1360:                for (int i = 0; i < removedObjects.size(); i++)
1361:                    context.getLockTracker().unlock(
1362:                            ((ObjectTracker.ObjectWrapper) removedObjects
1363:                                    .get(i)).getObject());
1364:                List addedObjects = transaction.getSavedObjects();
1365:                for (int i = 0; i < addedObjects.size(); i++)
1366:                    context.getLockTracker().unlock(
1367:                            ((ObjectTracker.ObjectWrapper) addedObjects.get(i))
1368:                                    .getObject());
1369:                // }}}
1370:            }
1371:
1372:            /**
1373:             * Notify server of all objects that changed. Server keeps track of the last modifications
1374:             * to know which object versions are current. To do this, we notify the server, that
1375:             * this transaction is about to be commited. The object modifications will be finalized,
1376:             * if the server receives the endCommit() call. If that event fails to be delivered,
1377:             * the server will mark the changes as unknown, and will check from the database to be
1378:             * sure.
1379:             */
1380:            private void notifyServerOfChanges(Transaction transaction) {
1381:                // {{{ Notify server of object changes inside transaction
1382:                Vector objects = new Vector();
1383:                objects.addAll(transaction.getRemovedObjects());
1384:                objects.addAll(transaction.getSavedObjects());
1385:                List metas = new Vector();
1386:                for (int i = 0; i < objects.size(); i++)
1387:                    metas
1388:                            .add(getPersistenceMetaData(((ObjectTracker.ObjectWrapper) objects
1389:                                    .get(i)).getObject()));
1390:                context.getNodeManager().notifyChange(metas,
1391:                        transaction.getSerial(), transaction.getEndSerial());
1392:                // }}}
1393:            }
1394:
1395:            /**
1396:             * Modify object tracker's metadata for the transaction's changes.
1397:             */
1398:            private void modifyMetaData(Transaction transaction) {
1399:                // {{{ Modify transaction objects' metadata
1400:                for (int i = 0; i < transaction.getRemovedObjects().size(); i++) {
1401:                    Object obj = ((ObjectTracker.ObjectWrapper) transaction
1402:                            .getRemovedObjects().get(i)).getObject();
1403:                    PersistenceMetaData metaData = getPersistenceMetaData(obj);
1404:                    metaData.setPersistenceEnd(transaction.getEndSerial());
1405:                }
1406:                for (int i = 0; i < transaction.getSavedObjects().size(); i++) {
1407:                    Object obj = ((ObjectTracker.ObjectWrapper) transaction
1408:                            .getSavedObjects().get(i)).getObject();
1409:                    PersistenceMetaData metaData = getPersistenceMetaData(obj);
1410:                    metaData.setQuerySerial(transaction.getEndSerial());
1411:                    metaData.setPersistenceStart(transaction.getEndSerial());
1412:                    // If object was saved, it also was removed if it existed, so
1413:                    // if the object still exists, clear the end serial
1414:                    if (context.getObjectTracker().exists(obj))
1415:                        metaData.setPersistenceEnd(null);
1416:                }
1417:                // }}}
1418:            }
1419:
1420:            /**
1421:             * Rollback a transaction. Rollback has to only unlock objects
1422:             * the transaction carries, and call the database rollback.
1423:             */
1424:            void rollback(Transaction transaction) {
1425:                // {{{ Rollback transaction
1426:                try {
1427:                    logger.debug("store rollback runs on transaction: "
1428:                            + transaction);
1429:                    // Call transaction tracker rollback (which also calls notify threads)
1430:                    context.getTransactionTracker().internalRollback(
1431:                            transaction);
1432:                } finally {
1433:                    // Unlock objects
1434:                    unlockObjects(transaction);
1435:                }
1436:                // }}}
1437:            }
1438:
1439:            /**
1440:             * Commit a transaction. This method is necessary, because on the
1441:             * end of a transaction, the store must set all persistence_start
1442:             * and persistence_end date/serials to the actual close-serial
1443:             * of transaction.
1444:             */
1445:            void commit(Transaction transaction) {
1446:                // {{{ Commit transaction
1447:                logger
1448:                        .debug("store commit runs on transaction: "
1449:                                + transaction);
1450:                Long endSerial = null;
1451:                boolean success = true;
1452:                try {
1453:                    Throwable failureCause = null;
1454:                    if (transaction.isRollbackOnly())
1455:                        success = false;
1456:                    // We need a serial for the transaction to end. This serial
1457:                    // will denote the commit itself. The beginning of a commit
1458:                    // must be marked, because while the commit is running, no
1459:                    // query can execute which has a higher serial, because this
1460:                    // would mean the query will change once this commit finishes.
1461:                    endSerial = context.getNodeManager().startCommit(
1462:                            context.getNodeManager().getNodeIndex());
1463:                    transaction.setEndSerial(endSerial);
1464:                    // All operations done now must finish, if an exception occurs,
1465:                    // roll it back manually and throw exception.
1466:                    try {
1467:                        // Save: set startdates where startdate is maxdate
1468:                        List saveTables = transaction.getSaveTables();
1469:                        HashMap keys = new HashMap();
1470:                        keys.put("persistence_txstartid", transaction
1471:                                .getSerial());
1472:                        HashMap changes = new HashMap();
1473:                        changes.put("persistence_start", endSerial);
1474:                        for (int i = 0; i < saveTables.size(); i++) {
1475:                            String tableName = (String) saveTables.get(i);
1476:                            logger.debug("fixing save table: " + tableName);
1477:                            context.getDatabase().save(transaction, tableName,
1478:                                    keys, changes);
1479:                            // Notify cache, that table changed for everyone
1480:                            context.getCache().updateEntries(tableName,
1481:                                    endSerial);
1482:                            context.getNodeManager().updateEntries(tableName,
1483:                                    endSerial);
1484:                        }
1485:                        // Remove: set enddates where txenddate is not maxdate
1486:                        List removeTables = transaction.getRemoveTables();
1487:                        keys = new HashMap();
1488:                        keys
1489:                                .put("persistence_txendid", transaction
1490:                                        .getSerial());
1491:                        changes = new HashMap();
1492:                        changes.put("persistence_end", endSerial);
1493:                        for (int i = 0; i < removeTables.size(); i++) {
1494:                            String tableName = (String) removeTables.get(i);
1495:                            logger.debug("fixing remove table: " + tableName);
1496:                            context.getDatabase().save(transaction, tableName,
1497:                                    keys, changes);
1498:                            // Notify cache, that table changed for everyone
1499:                            context.getCache().updateEntries(tableName,
1500:                                    endSerial);
1501:                            context.getNodeManager().updateEntries(tableName,
1502:                                    endSerial);
1503:                        }
1504:                        // Notify the server of all objects that changed. This operation
1505:                        // must be before the commit physically occurs, because this notification
1506:                        // will cause the server to know which objects are modified.
1507:                        notifyServerOfChanges(transaction);
1508:                    } catch (Exception e) {
1509:                        failureCause = e;
1510:                        success = false;
1511:                    }
1512:                    // Call transaction tracker commit (which also calls notify threads)
1513:                    if (success) {
1514:                        success = false;
1515:                        logger
1516:                                .debug("store calls internal commit on transaction: "
1517:                                        + transaction);
1518:                        context.getTransactionTracker().internalCommit(
1519:                                transaction);
1520:                        success = true; // Commit really ran
1521:                    } else {
1522:                        // Rollback and throw exception
1523:                        logger
1524:                                .debug("store commit calls rollback on transaction: "
1525:                                        + transaction);
1526:                        context.getTransactionTracker().internalRollback(
1527:                                transaction);
1528:                        throw new StoreException(
1529:                                "exception while commiting transaction.",
1530:                                failureCause);
1531:                    }
1532:                } finally {
1533:                    // If the commit was successfull, we must update objects'
1534:                    // metadata to reflect changes.
1535:                    if (success)
1536:                        modifyMetaData(transaction);
1537:                    // Exit commit semaphore. If the end of the commit is reached,
1538:                    // queries can be initiated with this serial, because all information
1539:                    // is readyly commited.
1540:                    if (endSerial != null)
1541:                        context.getNodeManager().endCommit(
1542:                                context.getNodeManager().getNodeIndex(),
1543:                                endSerial);
1544:                    // Unlock objects. Unlock event must come after the commit has been
1545:                    // closed. If this event does not reach the server, the communication
1546:                    // error will cause the server to unlock all objects anyway.
1547:                    unlockObjects(transaction);
1548:                    // Log end
1549:                    logger.debug("store commit ended on transaction: "
1550:                            + transaction);
1551:                }
1552:                // }}}
1553:            }
1554:
1555:            private class IdsEntry {
1556:                public Set ids;
1557:                public ClassInfo classInfo;
1558:                public Map objects;
1559:
1560:                public IdsEntry(Map objects, Set ids, ClassInfo classInfo) {
1561:                    this .objects = objects;
1562:                    this .ids = ids;
1563:                    this .classInfo = classInfo;
1564:                }
1565:            }
1566:
1567:            /**
1568:             * This is a shutdown logic, which simply calls <code>close()</code>
1569:             * when the JVM exists.
1570:             */
1571:            private class ShutdownProcess implements  Runnable {
1572:                public void run() {
1573:                    close(true);
1574:                }
1575:            }
1576:
1577:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.