Source Code Cross Referenced for ClassTracker.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) 


001:        /**
002:         * Copyright (C) 2006 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 3 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;
018:
019:        import java.util.Map;
020:        import java.util.HashMap;
021:        import java.util.List;
022:        import java.util.Iterator;
023:        import java.util.Date;
024:        import java.util.Vector;
025:        import java.util.HashSet;
026:        import java.util.Stack;
027:        import java.lang.reflect.Modifier;
028:        import hu.netmind.persistence.parser.QueryStatement;
029:        import org.apache.log4j.Logger;
030:
031:        /**
032:         * This class keeps track of different classes and objects. It's main
033:         * purpose is to implement object reflection based logic, and provide
034:         * type information.
035:         * @author Brautigam Robert
036:         * @version Revision: $Revision$
037:         */
038:        public class ClassTracker implements  TransactionListener {
039:            private static Logger logger = Logger.getLogger(ClassTracker.class);
040:
041:            public final static int TYPE_NONE = 0;
042:            public final static int TYPE_PRIMITIVE = 1;
043:            public final static int TYPE_HANDLED = 2;
044:            public final static int TYPE_OBJECT = 4;
045:            public final static int TYPE_JAVA = 6;
046:            public final static int TYPE_RESERVED = 5;
047:
048:            private StoreContext context;
049:            private Map classInfos; // Entry->Info mapping
050:            private Map tableEntries; // Table name->Entry mapping
051:            private Map transactionTableEntries; // Transaction->(Name->Entry)
052:            private Map relatedClassEntries; // Entry->Entry list
053:            private Map rootClassEntries; // Entry->Entry list
054:
055:            public ClassTracker(StoreContext context) {
056:                this .context = context;
057:                context.getTransactionTracker().addListener(this );
058:                classInfos = new HashMap();
059:                tableEntries = new HashMap();
060:                transactionTableEntries = new HashMap();
061:                relatedClassEntries = new HashMap();
062:                rootClassEntries = new HashMap();
063:                // Load class names
064:                Transaction transaction = context.getTransactionTracker()
065:                        .getTransaction(TransactionTracker.TX_REQUIRED);
066:                transaction.begin();
067:                try {
068:                    loadTableNames(transaction);
069:                } catch (StoreException e) {
070:                    transaction.markRollbackOnly();
071:                    throw e;
072:                } catch (Throwable e) {
073:                    transaction.markRollbackOnly();
074:                    throw new StoreException("unexcepted error", e);
075:                } finally {
076:                    transaction.commit();
077:                }
078:            }
079:
080:            /**
081:             * Return whether the given class is a primitive type.
082:             */
083:            public static boolean isPrimitive(Class clazz) {
084:                if (clazz == null)
085:                    return false;
086:                return (clazz.equals(int.class)) || (clazz.equals(short.class))
087:                        || (clazz.equals(byte.class))
088:                        || (clazz.equals(long.class))
089:                        || (clazz.equals(float.class))
090:                        || (clazz.equals(double.class))
091:                        || (clazz.equals(char.class))
092:                        || (clazz.equals(boolean.class))
093:                        || (clazz.equals(Integer.class))
094:                        || (clazz.equals(Short.class))
095:                        || (clazz.equals(Byte.class))
096:                        || (clazz.equals(Long.class))
097:                        || (clazz.equals(Float.class))
098:                        || (clazz.equals(Double.class))
099:                        || (clazz.equals(Character.class))
100:                        || (clazz.equals(String.class))
101:                        || (clazz.equals(Boolean.class))
102:                        || (clazz.equals(byte[].class))
103:                        || (clazz.equals(Date.class))
104:                        || clazz.equals(Character.class);
105:            }
106:
107:            /**
108:             * Get the type id of given class.
109:             */
110:            public int getType(Class clazz) {
111:                if (clazz == null)
112:                    return TYPE_NONE;
113:                if (context.getTypeHandlerTracker().isHandled(clazz))
114:                    return TYPE_HANDLED;
115:                if (isPrimitive(clazz))
116:                    return TYPE_PRIMITIVE;
117:                // Array types not allowed
118:                if (clazz.getName().startsWith("["))
119:                    return TYPE_RESERVED;
120:                // The rest is custom objects.
121:                return TYPE_OBJECT;
122:            }
123:
124:            /**
125:             * Get class info for a class entry.
126:             */
127:            public ClassInfo getClassInfo(ClassEntry entry) {
128:                return getClassInfo(entry.getSourceClass(), entry
129:                        .getDynamicName());
130:            }
131:
132:            /**
133:             * Get class info for an object and it's class.
134:             */
135:            public ClassInfo getClassInfo(Class clazz, Object obj) {
136:                if ((obj instanceof  DynamicObject)
137:                        && (obj.getClass().equals(clazz)))
138:                    return getClassInfo(clazz, ((DynamicObject) obj)
139:                            .getPersistenceDynamicName());
140:                else
141:                    return getClassInfo(clazz, null);
142:            }
143:
144:            /**
145:             * Get class info from a string.
146:             */
147:            public ClassInfo getClassInfo(String className, String dynamicName) {
148:                if (className == null)
149:                    return null;
150:                try {
151:                    Class clazz = Class.forName(className);
152:                    return getClassInfo(clazz, dynamicName);
153:                } catch (StoreException e) {
154:                    throw e;
155:                } catch (Exception e) {
156:                    throw new StoreException(
157:                            "class cannot be located with name '" + className
158:                                    + "'", e);
159:                }
160:            }
161:
162:            /**
163:             * Get all related classes to the given entry. Related class entries are
164:             * all given class' super- and sub-classes which are all storable.
165:             * Calling this method on non-storable entry will result in an undefined
166:             * result.
167:             */
168:            public List getRelatedClassEntries(ClassEntry entry) {
169:                ClassInfo info = getClassInfo(entry); // Register
170:                List result = (List) relatedClassEntries.get(entry);
171:                if (result == null)
172:                    return new Vector();
173:                return result;
174:            }
175:
176:            /**
177:             * Get all subclasses of given entry, including itself.
178:             */
179:            public List getSubClasses(ClassEntry entry) {
180:                List relatedEntries = getRelatedClassEntries(entry);
181:                Vector result = new Vector();
182:                result.add(entry.getSourceClass());
183:                for (int i = 0; i < relatedEntries.size(); i++) {
184:                    ClassEntry subEntry = (ClassEntry) relatedEntries.get(i);
185:                    if ((entry.getSourceClass().isAssignableFrom(subEntry
186:                            .getSourceClass()))
187:                            && (!result.contains(subEntry.getSourceClass())))
188:                        result.add(subEntry.getSourceClass());
189:                }
190:                if (logger.isDebugEnabled())
191:                    logger.debug("returning subclasses: " + result + ", for: "
192:                            + entry);
193:                return result;
194:            }
195:
196:            /**
197:             * Get all storable roots for given entry. A storable root for a
198:             * storable entry is itself. A non-storable entry (such as java.lang.Object)
199:             * will have potentially a lot of storable roots: All classes in the
200:             * class hierarchy which are storable, but have non-storable superclasses.
201:             * So, storable roots are the first storable entry in a class hierarchy
202:             * path (roots of the storable sub-forest). When a query is received 
203:             * for a non-stored class, the query will split into queries for all
204:             * storable roots.
205:             */
206:            public List getStorableRootClassEntries(ClassEntry entry) {
207:                ClassInfo info = getClassInfo(entry); // Register
208:                List result = (List) rootClassEntries.get(entry);
209:                if (result == null)
210:                    return new Vector();
211:                return result;
212:            }
213:
214:            /**
215:             * Get class information object of given class. Also, if class 
216:             * information does not exist, it will be created.
217:             */
218:            public ClassInfo getClassInfo(Class clazz, String dynamicName) {
219:                if (clazz == null)
220:                    return null;
221:                ClassInfo result = null;
222:                // Create entry
223:                ClassEntry entry = new ClassEntry(clazz, dynamicName);
224:                // Compute full name
225:                String fullClassName = entry.getFullName();
226:                // Get the classinfo from cache. If it's not there, then
227:                // create it.
228:                synchronized (classInfos) {
229:                    result = (ClassInfo) classInfos.get(fullClassName);
230:                    if (result == null) {
231:                        logger.debug("class info for class '" + fullClassName
232:                                + "' did not exist, creating.");
233:                        // Not present, so create.
234:                        // First, the superclasses if this is a static class,
235:                        // or the common class under the dynamic class.
236:                        if (entry.getDynamicName() == null) {
237:                            // A non-dynamic class has it's superclass as 
238:                            // superclass (no surprises here)
239:                            if (getType(clazz.getSuperclass()) == TYPE_PRIMITIVE)
240:                                throw new StoreException("class " + clazz
241:                                        + " subclassed a primitive type ("
242:                                        + clazz.getSuperclass()
243:                                        + "), this is not allowed.");
244:                            getClassInfo(clazz.getSuperclass(), null);
245:                            // Now register all interface classes also, so
246:                            // we can later select this class with it's interfaces
247:                            // also
248:                            Class interfaces[] = clazz.getInterfaces();
249:                            for (int i = 0; i < interfaces.length; i++)
250:                                getClassInfo(interfaces[i], null);
251:                        } else {
252:                            // Dynamic class has the class with 'null' dynamic name
253:                            // as superclass. It has no interface classes directly
254:                            // associated.
255:                            getClassInfo(clazz, null);
256:                        }
257:                        // Now register the class itself
258:                        result = new ClassInfo(context, entry);
259:                        classInfos.put(fullClassName, result);
260:                        // Update class relations
261:                        updateClassEntryRelations(entry);
262:                    } else {
263:                        // If the class info did not change, then
264:                        // return them, else update the classinfo.
265:                        if (result.hasChanged()) {
266:                            logger.debug("class info for class '" + clazz
267:                                    + "' has changed.");
268:                            // Allocate new instance and update classinfo table
269:                            result = new ClassInfo(context, entry);
270:                            // Replace old info
271:                            classInfos.put(fullClassName, result);
272:                        } else {
273:                            logger.debug("class info for class '" + clazz
274:                                    + "' did not change, returning.");
275:                            // Return old class info, because nothing changed.
276:                            return result;
277:                        }
278:                    }
279:                    // Check whether the class is valid, if not, remove it from the
280:                    // infos.
281:                    try {
282:                        isValid(result);
283:                    } catch (StoreException e) {
284:                        classInfos.remove(fullClassName);
285:                        throw e;
286:                    }
287:                }
288:                // Check whether this class is entitled to a table.
289:                // Interface classes, and reserved classes are not, so the method
290:                // will return, and not create the table for it (note, that the
291:                // class relations were updated, so we remember this class none
292:                // theless)
293:                boolean create = result.getSourceEntry().isStorable();
294:                // Check that this is not an inner or anonymous class, which are
295:                // not handled, so an error must be generated.
296:                if (clazz.getDeclaringClass() != null)
297:                    throw new StoreException(
298:                            "class '"
299:                                    + clazz
300:                                    + "' is an inner or anonymous class, it won't be handled.");
301:                // Check that the object is valid, meaning it has a default
302:                // constructor (if it's non-abstract)
303:                if ((create) && (!Modifier.isAbstract(clazz.getModifiers()))
304:                        && (getType(clazz) == TYPE_OBJECT)) {
305:                    // Only check, if it will be storable.
306:                    try {
307:                        clazz.getConstructor(new Class[0]);
308:                    } catch (NoSuchMethodException e) {
309:                        throw new StoreException(
310:                                "class invalid, it had no default constructor: "
311:                                        + clazz);
312:                    }
313:                }
314:                // If all checks are successful, and the class info was not yet present, 
315:                // we should ensure that the table exists in database.
316:                Transaction transaction = context.getTransactionTracker()
317:                        .getTransaction(TransactionTracker.TX_NEW);
318:                transaction.begin();
319:                try {
320:                    // Generate table schemas, one for class an one for each list or
321:                    // map type
322:                    Iterator iterator = result.getStrictAttributeNames(entry)
323:                            .iterator();
324:                    HashMap attributeTypes = new HashMap();
325:                    while (iterator.hasNext()) {
326:                        String attributeName = iterator.next().toString();
327:                        Class type = result.getAttributeType(attributeName);
328:                        switch (getType(type)) {
329:                        case TYPE_PRIMITIVE:
330:                            attributeTypes.put(attributeName, type);
331:                            break;
332:                        case TYPE_OBJECT:
333:                            attributeTypes.put(attributeName, Long.class);
334:                            break;
335:                        case TYPE_HANDLED:
336:                            TypeHandler handler = context
337:                                    .getTypeHandlerTracker().getHandler(type);
338:                            handler.ensureTableExists(result, attributeName,
339:                                    create);
340:                            attributeTypes.putAll(handler
341:                                    .getAttributeTypes(attributeName));
342:                            break;
343:                        default:
344:                            // Unknown type, leave it
345:                            logger.warn(clazz.toString()
346:                                    + " has attribute of unhandled class: "
347:                                    + type);
348:                            break;
349:                        }
350:                    }
351:                    // Remove reserved names
352:                    Iterator typesIterator = attributeTypes.keySet().iterator();
353:                    while (typesIterator.hasNext()) {
354:                        String name = (String) typesIterator.next();
355:                        if (name.startsWith("persistence"))
356:                            typesIterator.remove();
357:                    }
358:                    // Make object table
359:                    Vector objectKeys = new Vector();
360:                    objectKeys.add("persistence_id");
361:                    objectKeys.add("persistence_txstart");
362:                    attributeTypes.put("persistence_id", Long.class);
363:                    attributeTypes.put("persistence_start", Long.class);
364:                    attributeTypes.put("persistence_end", Long.class);
365:                    attributeTypes.put("persistence_txstartid", Long.class);
366:                    attributeTypes.put("persistence_txstart", Long.class);
367:                    attributeTypes.put("persistence_txendid", Long.class);
368:                    attributeTypes.put("persistence_txend", Long.class);
369:                    logger.debug("creating table for class: " + clazz
370:                            + ", fields: " + attributeTypes + ", keys: "
371:                            + objectKeys);
372:                    context.getDatabase().ensureTable(transaction,
373:                            result.getTableName(entry), attributeTypes,
374:                            objectKeys, create);
375:                    // Add new table to table names if not present.
376:                    // Look in the global map, then in the transaction map
377:                    boolean classPresent = false;
378:                    synchronized (tableEntries) {
379:                        classPresent = tableEntries.containsKey(result
380:                                .getTableName(entry));
381:                        if (!classPresent) {
382:                            Map transactionMap = (Map) transactionTableEntries
383:                                    .get(transaction);
384:                            if (transactionMap == null) {
385:                                transactionMap = new HashMap();
386:                                transactionTableEntries.put(transaction,
387:                                        transactionMap);
388:                            } else {
389:                                classPresent = transactionMap
390:                                        .containsKey(result.getTableName(entry));
391:                            }
392:                            if (!classPresent)
393:                                transactionMap.put(result.getTableName(entry),
394:                                        entry);
395:                        }
396:                    }
397:                    // Add new table to table names table if not yet present.
398:                    // To work around the following problem, the class is tried
399:                    // to be removed first: When two nodes are started with new
400:                    // classes, the first is able to insert the class info, but
401:                    // the second will not be able to insert it, because of duplicate
402:                    // keys.
403:                    if (!classPresent) {
404:                        logger
405:                                .debug("entry: "
406:                                        + result.getSourceEntry()
407:                                        + " was not present in database, so adding. It received tablename: "
408:                                        + result.getTableName(entry));
409:                        Map attrs = new HashMap();
410:                        attrs.put("tablename", result.getTableName(entry));
411:                        context.getDatabase().remove(transaction,
412:                                "persistence_classes", attrs);
413:                        attrs.put("classname", result.getSourceEntry()
414:                                .getSourceClass().getName());
415:                        attrs.put("dynamic", dynamicName);
416:                        context.getDatabase().insert(transaction,
417:                                "persistence_classes", attrs);
418:                    }
419:                } catch (StoreException e) {
420:                    transaction.markRollbackOnly();
421:                    throw e;
422:                } catch (Throwable e) {
423:                    transaction.markRollbackOnly();
424:                    throw new StoreException("unexpected error.", e);
425:                } finally {
426:                    transaction.commit();
427:                }
428:                // Return class info
429:                logger.debug("returning classinfo for class: "
430:                        + clazz.getName() + ": " + result);
431:                return result;
432:            }
433:
434:            /**
435:             * Determine whether a class is valid. The current algorithm checks:
436:             * <ul>
437:             *    <li>Class can't be empty (abstract and no attributes) and have dynamic attributes.</li>
438:             *    <li>Class can't have an attribute which is contained in a related
439:             *    class.</li>
440:             * </ul>
441:             * If the class is invalid, a StoreException is thrown with the description
442:             * of the error.
443:             */
444:            private void isValid(ClassInfo info) {
445:                // Check emptyness against dynamic attributes
446:                if ((info.getSourceEntry().isEmpty())
447:                        && (info.getSourceEntry().hasDynamicAttributes()))
448:                    throw new StoreException(
449:                            "class info: "
450:                                    + info
451:                                    + " is empty (abstract and has no static attributes), but has dynamic attributes. "
452:                                    + "This is not allowed, because it could be mistaken for a non-storable class. If it should be stored, please declare it non-abstract, "
453:                                    + "or if it has important attributes, declare them static.");
454:                // Search for common attributes
455:                List attributeNames = info.getStrictAttributeNames(info
456:                        .getSourceEntry());
457:                // Search for the root superentry.
458:                ClassEntry entry = info.getSourceEntry();
459:                ClassEntry super Entry = entry.getSuperEntry();
460:                while ((super Entry != null) && (super Entry.isStorable())) {
461:                    entry = super Entry;
462:                    super Entry = entry.getSuperEntry();
463:                }
464:                // Now check all related classes and ensure, they don't have
465:                // any attributes common with this one.
466:                List relatedEntries = getRelatedClassEntries(entry);
467:                logger
468:                        .debug("making sure, that no common attributes are present for entry: "
469:                                + info.getSourceEntry()
470:                                + ", with: "
471:                                + relatedEntries);
472:                for (int i = 0; i < relatedEntries.size(); i++) {
473:                    ClassInfo check = getClassInfo((ClassEntry) relatedEntries
474:                            .get(i));
475:                    if (check.equals(info))
476:                        continue;
477:                    for (int o = 0; o < attributeNames.size(); o++) {
478:                        String attributeName = (String) attributeNames.get(o);
479:                        if (check.getStrictAttributeNames(
480:                                check.getSourceEntry()).contains(attributeName))
481:                            throw new StoreException(
482:                                    "class info: "
483:                                            + info
484:                                            + " had a common attribute '"
485:                                            + attributeName
486:                                            + "' with '"
487:                                            + check
488:                                            + "', and common attributes in related classes are currently not allowed");
489:                    }
490:                }
491:            }
492:
493:            /**
494:             * Get the class info for a given table name.
495:             */
496:            public ClassInfo getTableClassInfo(String tableName) {
497:                ClassEntry entry = null;
498:                synchronized (tableEntries) {
499:                    entry = (ClassEntry) tableEntries.get(tableName);
500:                }
501:                if (entry == null)
502:                    return null;
503:                return getClassInfo(entry.getSourceClass(), entry
504:                        .getDynamicName());
505:            }
506:
507:            private void loadTableNames(Transaction transaction) {
508:                // First ensure that table exists
509:                HashMap tableAttrs = new HashMap();
510:                tableAttrs.put("tablename", String.class);
511:                tableAttrs.put("classname", String.class);
512:                tableAttrs.put("dynamic", String.class);
513:                Vector tableKeys = new Vector();
514:                tableKeys.add("tablename");
515:                context.getDatabase().ensureTable(transaction,
516:                        "persistence_classes", tableAttrs, tableKeys, true);
517:                // Load table
518:                QueryStatement stmt = new QueryStatement("persistence_classes",
519:                        null, null);
520:                SearchResult result = context.getDatabase().search(transaction,
521:                        stmt, null);
522:                for (int i = 0; i < result.getResult().size(); i++) {
523:                    Map attributes = (Map) result.getResult().get(i);
524:                    String tableName = (String) attributes.get("tablename");
525:                    String className = (String) attributes.get("classname");
526:                    String dynamicName = (String) attributes.get("dynamic");
527:                    try {
528:                        Class clazz = Class.forName(className);
529:                        ClassEntry entry = new ClassEntry(clazz, dynamicName);
530:                        tableEntries.put(tableName, entry);
531:                        updateClassEntryRelations(entry);
532:                        logger.debug("class tables loaded known class name: "
533:                                + className + ", class: " + clazz);
534:                    } catch (StoreException e) {
535:                        throw e;
536:                    } catch (Exception e) {
537:                        logger
538:                                .warn("could not find class ("
539:                                        + className
540:                                        + ") to table: "
541:                                        + tableName
542:                                        + ". This may only mean that a class was removed, but there are traces in the database for it, in this case, you may disregard this message: "
543:                                        + e.getMessage() + " ("
544:                                        + e.getClass().getName() + ")");
545:                    }
546:                }
547:            }
548:
549:            /**
550:             * Update class relations for a given class. This means insert this class
551:             * as related to all subclasses, and mark all subclasses related for this
552:             * class.
553:             */
554:            private void updateClassEntryRelations(ClassEntry entry) {
555:                // The first superclass differs when handling dynamic classes,
556:                // because then, the superclass is the ordinary class of dynamic class
557:                ClassEntry super Entry = entry.getSuperEntry();
558:                // Enter into graph walker as initial node
559:                Stack openEntries = new Stack();
560:                if (super Entry != null)
561:                    openEntries.push(super Entry);
562:                // This class will be potentially a storable root for all it's
563:                // interfaces. This is not yet sure, but we will check later.
564:                Vector storableSuperEntries = new Vector();
565:                Vector nonstorableSuperEntries = new Vector();
566:                for (int i = 0; i < entry.getSourceClass().getInterfaces().length; i++)
567:                    nonstorableSuperEntries.add(new ClassEntry(entry
568:                            .getSourceClass().getInterfaces()[i], null));
569:                // Go through all superclasses and insert this class
570:                // as related to them. Insert to interfaces and reserved classes
571:                // too, because we need to know if they are related.
572:                // The algorithm is a simple graph walker. All not yet visited
573:                // nodes are stored in openEntries, and visited one-by-one. Note, that
574:                // this works, because there is no cycle in the class hierarchy.
575:                while (!openEntries.empty()) {
576:                    // Get top
577:                    super Entry = (ClassEntry) openEntries.pop();
578:                    // Add entry to superclass' related entries
579:                    Vector classEntries = (Vector) relatedClassEntries
580:                            .get(super Entry);
581:                    if (classEntries == null) {
582:                        classEntries = new Vector();
583:                        relatedClassEntries.put(super Entry, classEntries);
584:                    }
585:                    if (!classEntries.contains(entry))
586:                        classEntries.add(entry);
587:                    // Insert into storable super entries if this entry
588:                    // is storable, to non-storable super if it's not.
589:                    if (super Entry.isStorable()) {
590:                        // Entry is storable, so we will enter this as a related
591:                        // class.
592:                        storableSuperEntries.add(super Entry);
593:                    } else {
594:                        // Entry is not storable, so potentially it's first
595:                        // storable root is this entry. Of course, if this entry
596:                        // has a storable root, then this is not the real root,
597:                        // but this will be detected later.
598:                        nonstorableSuperEntries.add(super Entry);
599:                    }
600:                    // Add superclass to open nodes
601:                    if (super Entry.getSourceClass().getSuperclass() != null)
602:                        openEntries.push(new ClassEntry(super Entry
603:                                .getSourceClass().getSuperclass(), null));
604:                    // Also add all interfaces as not visited
605:                    for (int i = 0; i < super Entry.getSourceClass()
606:                            .getInterfaces().length; i++)
607:                        openEntries.add(new ClassEntry(super Entry
608:                                .getSourceClass().getInterfaces()[i], null));
609:                }
610:                if (logger.isDebugEnabled())
611:                    logger.debug("class tracker determined superclasses for "
612:                            + entry + ", non-storable: "
613:                            + nonstorableSuperEntries + ", storable: "
614:                            + storableSuperEntries);
615:                // Now insert all storable superclasses as related to this class.
616:                // If this class is not storable, this should be empty anyway.
617:                Vector classEntries = (Vector) relatedClassEntries.get(entry);
618:                if (classEntries == null) {
619:                    classEntries = new Vector();
620:                    relatedClassEntries.put(entry, classEntries);
621:                }
622:                classEntries.removeAll(storableSuperEntries);
623:                classEntries.addAll(storableSuperEntries);
624:                // Now add this class as a storable root to all non-storable
625:                // superclasses and superinterfaces, which do not have a storable
626:                // root which is a superclass to this entry.
627:                if (entry.isStorable()) {
628:                    // This entry is storable, so it's only storable root is itself
629:                    List rootEntries = new Vector();
630:                    rootEntries.add(entry);
631:                    rootClassEntries.put(entry, rootEntries);
632:                    // Now check it's non-storable supers one-by-one to see
633:                    // if this class is really their storable root.
634:                    for (int i = 0; i < nonstorableSuperEntries.size(); i++) {
635:                        ClassEntry nonstorableSuperEntry = (ClassEntry) nonstorableSuperEntries
636:                                .get(i);
637:                        rootEntries = (List) rootClassEntries
638:                                .get(nonstorableSuperEntry);
639:                        if (rootEntries == null) {
640:                            // Entry did not have a storable root yet, so this is
641:                            // surely a storable root.
642:                            rootEntries = new Vector();
643:                            rootEntries.add(entry);
644:                            rootClassEntries.put(nonstorableSuperEntry,
645:                                    rootEntries);
646:                        } else {
647:                            // Entry has some storable roots, so check them.
648:                            // If a root is a superclass of this class, then this
649:                            // class is not the storable root.
650:                            // If a root is a subclass of this class, that should not
651:                            // happen, because superclasses are always handled first!
652:                            boolean hasSuperRoot = false;
653:                            for (int o = 0; (o < rootEntries.size())
654:                                    && (!hasSuperRoot); o++) {
655:                                ClassEntry rootEntry = (ClassEntry) rootEntries
656:                                        .get(o);
657:                                if (rootEntry.getSourceClass().equals(
658:                                        entry.getSourceClass())) {
659:                                    // The class is present as root. If this entry is
660:                                    // a dynamic subclass of this class, then it can't
661:                                    // be the root, because it superclass is already root.
662:                                    hasSuperRoot = true;
663:                                } else {
664:                                    if (rootEntry.getSourceClass()
665:                                            .isAssignableFrom(
666:                                                    entry.getSourceClass()))
667:                                        hasSuperRoot = true; // Real superclass found
668:                                    if (entry.getSourceClass()
669:                                            .isAssignableFrom(
670:                                                    rootEntry.getSourceClass())) {
671:                                        // Subclass found, so replace it
672:                                        logger.debug("replacing storable root "
673:                                                + rootEntry + ", with: "
674:                                                + entry);
675:                                        rootEntries.remove(o--); // Remove this, we found a more suitable root
676:                                    }
677:                                }
678:                            }
679:                            if (!hasSuperRoot) {
680:                                rootEntries.add(entry);
681:                                if (logger.isDebugEnabled())
682:                                    logger.debug("added " + entry
683:                                            + " as storable root for: "
684:                                            + nonstorableSuperEntry
685:                                            + ", roots until now: "
686:                                            + rootEntries);
687:                            }
688:                        }
689:                    }
690:                } else {
691:                    // This is a non-storable entry, so of course non of the
692:                    // non-storable superclasses will have this as storable root.
693:                }
694:            }
695:
696:            /**
697:             * Get a Class instance for a class name postfix. The given parameter
698:             * is treated as a postfix for a fully qualified class name. The postfix
699:             * is considered matching, when it contains whole class of package
700:             * qualifiers. For example: "book" matches "hu.netmind.persistence.Book"
701:             * class, but does not match "hu.netmind.persistence.CookBook". Also
702:             * "persistence.book" matches "hu.netmind.persistence.Book", but 
703:             * "tence.book" does not match to previous class.<br>
704:             * If no classes are found null is returned. If more than one matching
705:             * class is present, then one of them is returned (no guarantees which
706:             * one is picked).
707:             * @param postfix The class name postfix.
708:             * @return The class info for which the postfix applies, or null.
709:             */
710:            public ClassInfo getMatchingClassInfo(String postfix) {
711:                Transaction transaction = context.getTransactionTracker()
712:                        .getTransaction(TransactionTracker.TX_OPTIONAL);
713:                // Get a list of all known classes from database
714:                HashSet entries = new HashSet();
715:                synchronized (tableEntries) {
716:                    Map transactionMap = (Map) transactionTableEntries
717:                            .get(transaction);
718:                    if (transactionMap != null)
719:                        entries.addAll(transactionMap.values());
720:                    entries.addAll(relatedClassEntries.keySet());
721:                }
722:                // Search for given prefix
723:                postfix = postfix.toLowerCase();
724:                Iterator iterator = entries.iterator();
725:                while (iterator.hasNext()) {
726:                    // Check class
727:                    ClassEntry entry = (ClassEntry) iterator.next();
728:                    String className = entry.getFullName().toLowerCase();
729:                    if ((className.endsWith(postfix))
730:                            && ((className.length() == postfix.length()) || (className
731:                                    .charAt(className.length()
732:                                            - postfix.length() - 1) == '.')))
733:                        return getClassInfo(entry.getSourceClass(), entry
734:                                .getDynamicName());
735:                }
736:                // None found
737:                return null;
738:            }
739:
740:            /**
741:             * Activate table names added in the transaction.
742:             */
743:            public void transactionCommited(Transaction transaction) {
744:                synchronized (tableEntries) {
745:                    Map transactionMap = (Map) transactionTableEntries
746:                            .get(transaction);
747:                    if (transactionMap == null)
748:                        return;
749:                    tableEntries.putAll(transactionMap);
750:                    transactionTableEntries.remove(transaction);
751:                }
752:            }
753:
754:            /**
755:             * Discard table names added in the transaction.
756:             */
757:            public void transactionRolledback(Transaction transaction) {
758:                synchronized (tableEntries) {
759:                    transactionTableEntries.remove(transaction);
760:                }
761:            }
762:
763:            /**
764:             * Return whether given class can be dynamic.
765:             */
766:            public boolean isDynamicCanidate(Class clazz) {
767:                return DynamicObject.class.isAssignableFrom(clazz);
768:            }
769:
770:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.