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


001:        /**
002:         * Copyright (C) 2008 NetMind Consulting Bt.
003:         *
004:         * This library is free software; you can redistribute it and/or
005:         * modify it under the terms of the GNU Lesser General Public
006:         * License as published by the Free Software Foundation; either
007:         * version 2.1 of the License, or (at your option) any later version.
008:         *
009:         * This library is distributed in the hope that it will be useful,
010:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
011:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
012:         * Lesser General Public License for more details.
013:         *
014:         * You should have received a copy of the GNU Lesser General Public
015:         * License along with this library; if not, write to the Free Software
016:         * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
017:         */package hu.netmind.persistence.node;
018:
019:        import hu.netmind.persistence.*;
020:        import java.util.*;
021:        import org.apache.log4j.Logger;
022:
023:        /**
024:         * This class tracks changes to objects. It caches the most recent changes, but
025:         * also determines whether an object changed by reading the database.
026:         * @author Brautigam Robert
027:         * @version CVS Revision: $Revision$
028:         */
029:        public class ModificationTracker {
030:            public static long MAX_ENTRY_AGE = 30 * 60 * 1000;
031:            private static Logger logger = Logger
032:                    .getLogger(ModificationTracker.class);
033:
034:            private StoreContext context;
035:            private Map entriesById = new HashMap();
036:            private SortedSet entriesByDate = new TreeSet();
037:            private Map entriesByTxSerial = new HashMap();
038:            private Map entriesByClass = new HashMap();
039:            private Map lastSerialsByClass = new HashMap();
040:            private Long lastCacheRemovedSerial = null;
041:
042:            public ModificationTracker(StoreContext context) {
043:                this .context = context;
044:            }
045:
046:            private void modifyClassEntries(ModificationEntry entry, boolean add) {
047:                modifyClassEntries(entry.objectClass, entry, add);
048:            }
049:
050:            private void modifyClassEntries(Class currentClass,
051:                    ModificationEntry entry, boolean add) {
052:                if (currentClass == null)
053:                    return;
054:                // Do current class
055:                Set entries = (Set) entriesByClass.get(currentClass);
056:                if (entries == null) {
057:                    entries = new HashSet();
058:                    entriesByClass.put(currentClass, entries);
059:                }
060:                if (add)
061:                    entries.add(entry);
062:                else
063:                    entries.remove(entry);
064:                if (entries.size() == 0)
065:                    entriesByClass.remove(currentClass);
066:                // Do superclass
067:                modifyClassEntries(currentClass.getSuperclass(), entry, add);
068:                // Do interfaces
069:                Class interfaces[] = currentClass.getInterfaces();
070:                for (int i = 0; i < interfaces.length; i++)
071:                    modifyClassEntries(interfaces[i], entry, add);
072:            }
073:
074:            /**
075:             * This method registers "potential" change serials for a given id. The
076:             * change becomes final only when the transaction's commit ends.
077:             */
078:            public synchronized void changeCandidates(List metas,
079:                    Long endSerial, Long txSerial) {
080:                // Clear obsolate data
081:                while ((!entriesByDate.isEmpty())
082:                        && (System.currentTimeMillis()
083:                                - ((ModificationEntry) entriesByDate.first()).entryDate
084:                                        .getTime() > MAX_ENTRY_AGE)) {
085:                    // The first entry is obsolate, so clear it from the cache
086:                    ModificationEntry entry = (ModificationEntry) entriesByDate
087:                            .first();
088:                    entriesById.remove(entry.id);
089:                    entriesByDate.remove(entry);
090:                    if (entry.potentialTxSerial != null)
091:                        entriesByTxSerial.remove(entry.potentialTxSerial);
092:                    if (entry.lastChangeSerial != null)
093:                        lastCacheRemovedSerial = entry.lastChangeSerial;
094:                    modifyClassEntries(entry, false); // Remove
095:                }
096:                // Add all data to internal structures
097:                Vector entries = new Vector();
098:                entriesByTxSerial.put(txSerial, entries);
099:                for (int i = 0; i < metas.size(); i++) {
100:                    PersistenceMetaData meta = (PersistenceMetaData) metas
101:                            .get(i);
102:                    // Search or create entry
103:                    ModificationEntry entry = (ModificationEntry) entriesById
104:                            .get(meta.getPersistenceId());
105:                    if (entry == null) {
106:                        entry = new ModificationEntry();
107:                        entry.id = meta.getPersistenceId();
108:                        entry.objectClass = meta.getObjectClass();
109:                        entry.entryDate = new Date();
110:                        // Update data structure
111:                        entriesById.put(entry.id, entry);
112:                        entriesByDate.add(entry);
113:                        modifyClassEntries(entry, true);
114:                    }
115:                    entry.potentialChangeSerial = endSerial;
116:                    if (entry.potentialTxSerial != null)
117:                        entriesByTxSerial.remove(entry.potentialTxSerial); // Old tx overridden
118:                    entry.potentialTxSerial = txSerial;
119:                    entries.add(entry);
120:                }
121:            }
122:
123:            /**
124:             * Set definite last modification serial for a class, and for all superclasses
125:             * and interfaces.
126:             */
127:            private void setLastSerial(Class currentClass, Long serial) {
128:                if (currentClass == null)
129:                    return;
130:                // Handle class
131:                lastSerialsByClass.put(currentClass, serial);
132:                // Superclass
133:                setLastSerial(currentClass.getSuperclass(), serial);
134:                // Interfaces
135:                Class interfaces[] = currentClass.getInterfaces();
136:                for (int i = 0; i < interfaces.length; i++)
137:                    setLastSerial(interfaces[i], serial);
138:            }
139:
140:            /**
141:             * Ends a transaction, which means it finalizes the entries associated
142:             * with the given transaction.
143:             */
144:            public synchronized void endTransaction(Long txSerial) {
145:                // Get and remove entries from tx map
146:                List entries = (List) entriesByTxSerial.remove(txSerial);
147:                if (entries == null)
148:                    return; // No ids, probably it became obsolate
149:                // Go through entries of ids and actualize them
150:                for (int i = 0; i < entries.size(); i++) {
151:                    ModificationEntry entry = (ModificationEntry) entries
152:                            .get(i);
153:                    entry.lastChangeSerial = entry.potentialChangeSerial;
154:                    entry.potentialTxSerial = null;
155:                    // Insert into the 'persistent' class serial table.
156:                    setLastSerial(entry.objectClass, entry.lastChangeSerial);
157:                }
158:            }
159:
160:            /**
161:             * Return whether the given object changed since the given serial.
162:             */
163:            private boolean hasChanged(Long id, Class objectClass, Long serial) {
164:                // Try to get a modification entry if there is one for that object
165:                ModificationEntry entry = (ModificationEntry) entriesById
166:                        .get(id);
167:                // If there was no entry, then there was no modification
168:                // since the last removed serial. If the query serial is newer than
169:                // the last removed serial, then the object must be current.
170:                if ((entry == null)
171:                        && ((lastCacheRemovedSerial == null) || (serial
172:                                .longValue() > lastCacheRemovedSerial
173:                                .longValue()))) {
174:                    logger
175:                            .debug("there was no modification yet for this object in cache, it is current.");
176:                    return false;
177:                }
178:                // If there was no entry, and the query serial is old, we must check the
179:                // database whether that object is current. Note, that at this point the
180:                // object should be locked, or else this query can not tell for sure.
181:                if ((entry == null)
182:                        && (lastCacheRemovedSerial != null)
183:                        && (serial.longValue() <= lastCacheRemovedSerial
184:                                .longValue())) {
185:                    // Query the object now
186:                    Object current = context.getStore().findSingle(
187:                            "find " + objectClass.getName()
188:                                    + " where persistenceId = " + id);
189:                    PersistenceMetaData persistenceMeta = context.getStore()
190:                            .getPersistenceMetaData(current);
191:                    if (persistenceMeta.getPersistenceStart().longValue() <= serial
192:                            .longValue()) {
193:                        logger
194:                                .debug("object is old, but database said it is current.");
195:                        return false;
196:                    }
197:                }
198:                // If there is an enty, but it has potentialTxSerial, that means
199:                // it was in a transaction, which started commiting, but never finished.
200:                // The client disconnected, and the locks were relinquished. In this
201:                // case, we must query the database to make sure the transaction commited.
202:                if ((entry != null) && (entry.potentialTxSerial != null)) {
203:                    // Query the object now
204:                    Object current = context.getStore().findSingle(
205:                            "find " + objectClass.getName()
206:                                    + " where persistenceId = " + id);
207:                    PersistenceMetaData persistenceMeta = context.getStore()
208:                            .getPersistenceMetaData(current);
209:                    // If the metadata's serial equals the potentialChangeSerial, then commit
210:                    // the transaction manually.
211:                    if (persistenceMeta.getPersistenceStart().longValue() >= entry.potentialChangeSerial
212:                            .longValue()) {
213:                        // Transaction was committed successfully
214:                        endTransaction(entry.potentialTxSerial);
215:                    } else {
216:                        // Originating transaction failed. Remove those entries which do
217:                        // not have a lastChangeSerial, because those were not modified yet.
218:                        List entries = (List) entriesByTxSerial
219:                                .remove(entry.potentialTxSerial);
220:                        for (int i = 0; (entries != null)
221:                                && (i < entries.size()); i++) {
222:                            ModificationEntry removeEntry = (ModificationEntry) entries
223:                                    .get(i);
224:                            entriesById.remove(removeEntry.id);
225:                            entriesByDate.remove(removeEntry);
226:                            modifyClassEntries(removeEntry, false);
227:                        }
228:                    }
229:                    // Now compare the query serial with the object start serial
230:                    if (persistenceMeta.getPersistenceStart().longValue() <= serial
231:                            .longValue()) {
232:                        logger
233:                                .info("transaction commit not received for serial: "
234:                                        + entry.potentialTxSerial
235:                                        + ", but transaction commited according to database.");
236:                        return false;
237:                    }
238:                }
239:                // Default is to compare entry's last change serial with the query date.
240:                // At this point both must be present.
241:                if (entry.lastChangeSerial.longValue() <= serial.longValue()) {
242:                    logger
243:                            .debug("there was a change for that object in cache, but the object is newer.");
244:                    return false;
245:                }
246:                // Last default is return session info
247:                logger.debug("object was not current, fallthrough return");
248:                return true;
249:            }
250:
251:            /**
252:             * This is the query method of the tracker. It returns whether an object that was
253:             * queried from the database at a given time is the current version of the object
254:             * or not. Implementation note: Currently empty session infos are returned,
255:             * this may later change, if session infos will be also kept track of.
256:             * @return A SessionInfo object if the object is not current. The session info
257:             * may contain the last modification transaction's info, but this is not guaranteed.
258:             */
259:            public synchronized SessionInfo isCurrent(LockMetaData meta) {
260:                logger.debug("making sure meta: " + meta + " is current.");
261:                // If the startdate is not set, then object is not yet saved, it is current.
262:                if ((meta.getObjectId() != null)
263:                        && (meta.getStartSerial() == null)) {
264:                    logger
265:                            .debug("object is not yet persistent, so it is current.");
266:                    return null;
267:                }
268:                // If the end serial is set, then the object is definitely not current
269:                // (it is deleted)!
270:                if ((meta.getObjectId() != null)
271:                        && (meta.getEndSerial() != null)) {
272:                    logger
273:                            .debug("object has enddate set, it will not be considered current.");
274:                    return new SessionInfo();
275:                }
276:                // If the query date is not set, then we have no data to match to, return current.
277:                if (meta.getQuerySerial() == null) {
278:                    logger
279:                            .debug("metadata contained no query serial, returning 'current'.");
280:                    return null;
281:                }
282:                // If the meta represents an object, then
283:                // Test, whether entry indicates a change since the query of the meta we received
284:                if (meta.getObjectId() != null) {
285:                    if (!hasChanged(meta.getObjectId(), meta.getObjectClass(),
286:                            meta.getQuerySerial()))
287:                        return null;
288:                }
289:                // If the meta represents a class, then we must check each modification
290:                // which concerns this class, whether they newer are than the given serial.
291:                if (meta.getObjectId() == null) {
292:                    Set entries = (Set) entriesByClass.get(meta
293:                            .getObjectClass());
294:                    if (entries != null) {
295:                        // If the entries are not null, then check whether there was a valid
296:                        // modification.
297:                        Iterator entriesIterator = entries.iterator();
298:                        while (entriesIterator.hasNext()) {
299:                            ModificationEntry entry = (ModificationEntry) entriesIterator
300:                                    .next();
301:                            if (hasChanged(entry.id, entry.objectClass, meta
302:                                    .getQuerySerial()))
303:                                return new SessionInfo(); // Change of object is newer, so class changed
304:                        }
305:                    }
306:                    // Get the entries again. If the set is non-existent, then there are no valid
307:                    // changes for this class. It is possible the above code returned changes, but
308:                    // those were rolledback.
309:                    entries = (Set) entriesByClass.get(meta.getObjectClass());
310:                    if (entries == null) {
311:                        // There were no changes to the class, so determine from modification table.
312:                        // Modification table would not be enough by itself, because modifications may have
313:                        // been activated which the modification table does not contain. So the above
314:                        // is necessary.
315:                        Long lastTableSerial = (Long) lastSerialsByClass
316:                                .get(meta.getObjectClass());
317:                        if ((lastTableSerial == null)
318:                                || (lastTableSerial.longValue() < meta
319:                                        .getQuerySerial().longValue()))
320:                            return null;
321:                    } else {
322:                        // There were entries, and they indicate that this table is current
323:                        return null;
324:                    }
325:                }
326:                // Fallback (not current).
327:                return new SessionInfo();
328:            }
329:
330:            public class ModificationEntry implements  Comparable {
331:                public Long id;
332:                public Class objectClass;
333:                public Date entryDate;
334:                public Long lastChangeSerial;
335:                public Long potentialChangeSerial;
336:                public Long potentialTxSerial;
337:
338:                public int compareTo(Object entry) {
339:                    return entryDate
340:                            .compareTo(((ModificationEntry) entry).entryDate);
341:                }
342:
343:                public int hashCode() {
344:                    return id.hashCode();
345:                }
346:
347:                public boolean equals(Object o) {
348:                    return id == ((ModificationEntry) o).id;
349:                }
350:            }
351:
352:            static {
353:                try {
354:                    ResourceBundle config = ResourceBundle
355:                            .getBundle("beankeeper");
356:                    MAX_ENTRY_AGE = Long.valueOf(
357:                            config.getString("cache.modification_max_age"))
358:                            .longValue();
359:                } catch (Exception e) {
360:                    logger
361:                            .error(
362:                                    "could not load configuration, using hardcoded defaults",
363:                                    e);
364:                }
365:            }
366:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.