Source Code Cross Referenced for CDatabase.java in  » Database-DBMS » perst » org » garret » perst » continuous » 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 DBMS » perst » org.garret.perst.continuous 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package org.garret.perst.continuous;
002:
003:        import java.util.*;
004:        import java.io.*;
005:        import org.garret.perst.*;
006:        import org.garret.perst.impl.ClassDescriptor;
007:
008:        import org.apache.lucene.index.Term;
009:        import org.apache.lucene.index.IndexWriter;
010:        import org.apache.lucene.index.IndexReader;
011:        import org.apache.lucene.search.IndexSearcher;
012:        import org.apache.lucene.document.Document;
013:        import org.apache.lucene.document.DateTools;
014:        import org.apache.lucene.store.FSDirectory;
015:        import org.apache.lucene.store.Directory;
016:        import org.apache.lucene.queryParser.*;
017:        import org.apache.lucene.search.Hits;
018:        import org.apache.lucene.analysis.standard.StandardAnalyzer;
019:
020:        /**
021:         * <p>
022:         * This class emulates relational database on top of Perst storage
023:         * It maintains class extends and associated indices. Is supports JSQL and full text search queries.
024:         * This class provides version control system, optimistic access control and full text search (using Lucene).
025:         * </p><p>
026:         * All transactions are isolated from each other and do not see changes made by other transactions 
027:         * (even committed transactions if commit happens after start of this transaction).
028:         * The database keeps all versions of the object hich are grouped in version history.
029:         * Version history is linear which means that no branches in version tree are possible. 
030:         * When transaction tries to update some object then working copy of the current version is created.
031:         * Only this copy is changed, leaving all other versions unchanged. This modification will be visible only
032:         * for the current transaction. Created or modified objects are linked in indices when transaction is committed.
033:         * It means that application will not be able to find in the index just inserted or updated object. It has 
034:         * first to commit the current transaction. All working copies created during transaction execution
035:         * are linked in he list stored in transaction context which is in turn associated with the current thread. 
036:         * It means two things:
037:         * <ol>
038:         * <li>one thread can participate in only one transaction and one transaction can be controlled only by one thread</li>
039:         * <li>size of transaction is limited by the amount of memory available for the application</li>
040:         * </ol>
041:         * When transaction is committed, then all its working copies are inspected. If the last version in version history is 
042:         * not equal to one from which working copy was created then conflict is detected and ConflictException is thrown.
043:         * And if insertion of working copy in index will cause unique constraint violation then NotUniqueException is thrown.
044:         * If no conflicts are detected, then transaction is committed and each working copy becomes new version in correspondent 
045:         * version history.
046:         * </p><p>
047:         * Isolation of transaction provided by CDatabase class is based on multiversioning and modification of working copies
048:         * instead of original objects. Working copy is produced from the current version using clone (shallow copy) method.
049:         * Clone will work correctly if components of the objects have primitive type (int, double,...) or are immutable (String). 
050:         * Using java.util.Date type is possible only if you do not try to update its components. 
051:         * Instead of normal Java references, you should use references to correspondent CVersionHistory (it allows to preserve 
052:         * reference consistency if new version of reference object is created).
053:         * CDatabase class also supports links (components with Link type) and arrays. 
054:         * Fields with <i>value type</i> (class implementing IValue interface) can be used only if them are 
055:         * immutable or cloneable (implement org.garret.perst.ICloneable interface). 
056:         * Using any other data type including Perst collection classes is not supported and can cause unpredictable behavior.
057:         */
058:        public class CDatabase {
059:            /** 
060:             * Open the database. This method initialize database if it not initialized yet.
061:             * @param storage opened storage. Storage should be either empty (non-initialized, either
062:             * previously initialized by the this method. It is not possible to open storage with 
063:             * root object other than RootObject  created by this method.
064:             * @param fullTextIndexPath path to the directory containing Lucene database. If null, then Lucene index will be stored 
065:             * inside the main Perst storage.
066:             */
067:            public void open(Storage storage, String fullTextIndexPath) {
068:                this .storage = storage;
069:                storage.setProperty("perst.concurrent.iterator", Boolean.TRUE);
070:                root = (RootObject) storage.getRoot();
071:                typeMap = new HashMap<Class, TableDescriptor>();
072:                if (root == null) {
073:                    root = new RootObject(storage);
074:                    storage.setRoot(root);
075:                } else {
076:                    for (TableDescriptor desc : root.tables) {
077:                        typeMap.put(desc.type, desc);
078:                    }
079:                }
080:                openFullTextIndex(root, fullTextIndexPath);
081:                storage.commit();
082:            }
083:
084:            /**
085:             * Close the database. All uncommitted transaction will be aborted.
086:             * Close full text index and storage. 
087:             */
088:            public void close() {
089:                try {
090:                    if (indexWriter != null) {
091:                        indexWriter.close();
092:                        indexWriter = null;
093:                    }
094:                    if (indexReader != null) {
095:                        indexReader.close();
096:                        indexReader = null;
097:                    }
098:                    if (dir != null) {
099:                        dir.close();
100:                        dir = null;
101:                    }
102:                } catch (IOException x) {
103:                    throw new IOError(x);
104:                }
105:                storage.close();
106:                storage = null;
107:
108:                root = null;
109:                typeMap = null;
110:                analyzer = null;
111:            }
112:
113:            /**
114:             * Start new transaction. All changes can be made only within transaction context. 
115:             * Transaction context is associated with thread. It means that one thread can run only one transaction and
116:             * one transaction can be handled only by one thread. 
117:             * Transaction is observing database state at the moment of the transaction start. Any changes done by
118:             * other transactions (committed and uncommitted) after this moment are not visible for the current transaction.
119:             * CDatabase uses optimistic access control. It means that transaction may be aborted if conflict 
120:             * is detected during transaction commit.
121:             */
122:            public void beginTransaction() {
123:                beginTransaction(root.getLastTransactionId());
124:            }
125:
126:            /**
127:             * Start transaction with the given identifier. 
128:             * Identifier of the transaction can be obtained using CVersion.getTransactionId() method.
129:             * Starting transaction with ID of previously committed transaction allows to obtain snapshot
130:             * of the database at the moment of this transaction execution. All search methods
131:             * using default version selector (referencing current version) and CVersionHistory.getCurrent()
132:             * method will select versions which were current at the moment of this transaction execution.
133:             * @param transId identifier of previously committed transaction
134:             * @exception TransactionAlreadyStartedException when thread attempts to start new transaction without committing or 
135:             * aborting previous one. Transaction should be explicitly finished using CDatabase.commit or CDatabase.rollback method.
136:             * Each thread should start its own transaction, it is not possible to share the same transaction by more than one threads.
137:             * It is possible to call beginTransaction several times without commit or rollback only if previous transaction was read-only 
138:             * - didn't change any object
139:             */
140:            public synchronized void beginTransaction(long transId) {
141:                TransactionContext ctx = getTransactionContext();
142:                if (ctx == null) {
143:                    ctx = new TransactionContext(this );
144:                    transactionContext.set(ctx);
145:                }
146:                ctx.beginTransaction(transId, ++maxTransSeqNo);
147:            }
148:
149:            /**
150:             * Commit transaction. 
151:             * @exception ConflictExpetion is thrown if object modified by this transaction was also changed by some other 
152:             * previously committed transaction 
153:             * @exception NotUniqueException is thrown if indexable field with unique constraint of inserted or updated object 
154:             * contains the same value as current version of some other instance of this class (committed by another transact).
155:             * Please notice that CDatabase is not able to detect unique constraint violations within one transaction - if it
156:             * inserts two instances with same value of indexable field.
157:             * @return identifier assigned to this transaction or 0 if there is no active transaction. 
158:             * This identifier can be used to obtain snapshot of the database at the moment of this transaction execution.
159:             */
160:            public long commitTransaction() throws ConflictException,
161:                    NotUniqueException {
162:                TransactionContext ctx = getTransactionContext();
163:                if (ctx == null
164:                        || ctx.transId == TransactionContext.IMPLICIT_TRANSACTION_ID) {
165:                    return 0;
166:                }
167:                if (ctx.isEmptyTransaction() && !root.isModified()) {
168:                    ctx.endTransaction();
169:                    return ctx.transId;
170:                }
171:                root.exclusiveLock();
172:                try {
173:                    Collection<CVersion> workSet = ctx.getWorkingCopies();
174:                    long transId = ctx.transId;
175:                    for (CVersion v : workSet) {
176:                        if ((v.flags & CVersion.NEW) == 0
177:                                && v.history.getLast().transId > transId) {
178:                            throw new ConflictException(v);
179:                        }
180:                        TableDescriptor desc = lookupTable(v.getClass());
181:                        if (desc != null) {
182:                            desc.checkConstraints(v);
183:                        }
184:                    }
185:                    if (ctx.seqNo == minTransSeqNo) {
186:                        minTransSeqNo += 1;
187:                        if (ctx.transId > lastActiveTransId) {
188:                            lastActiveTransId = ctx.transId;
189:                        }
190:                    }
191:                    transId = root.newTransactionId();
192:                    for (CVersion v : workSet) {
193:                        CVersionHistory vh = v.history;
194:                        TableDescriptor desc = getTable(v.getClass());
195:                        int flags = v.flags;
196:
197:                        v.transId = transId;
198:                        v.id = vh.getNumberOfVersions() + 1;
199:                        v.flags &= ~(CVersion.WORKING_COPY | CVersion.NEW);
200:                        v.date = new Date();
201:                        vh.add(v);
202:
203:                        if ((flags & (CVersion.NEW | CVersion.DELETED)) == CVersion.NEW) {
204:                            desc.classExtent.add(vh);
205:                        } else if (vh.limited) {
206:                            CVersion old;
207:                            while ((old = vh.get(CVersion.FIRST_VERSION_ID)).transId < lastActiveTransId) {
208:                                desc.excludeFromIndices(old);
209:                                excludeFromFullTextIndex(desc, old);
210:                                vh.remove(CVersion.FIRST_VERSION_ID);
211:                                old.deallocate();
212:                            }
213:                        }
214:                        if ((flags & CVersion.DELETED) == 0) {
215:                            desc.includeInIndices(v);
216:                            includeInFullTextIndex(desc.buildDocument(v));
217:                        }
218:                    }
219:                    try {
220:                        if (indexWriter != null) {
221:                            indexWriter.close();
222:                            indexWriter = null;
223:                        }
224:                    } catch (IOException x) {
225:                        throw new IOError(x);
226:                    }
227:                    storage.commit();
228:                    return transId;
229:                } finally {
230:                    root.unlock();
231:                    ctx.endTransaction();
232:                }
233:            }
234:
235:            /**
236:             * Rollback transaction
237:             */
238:            public void rollbackTransaction() {
239:                TransactionContext ctx = getTransactionContext();
240:                if (ctx != null) {
241:                    root.exclusiveLock();
242:                    try {
243:                        if (ctx.seqNo == minTransSeqNo) {
244:                            minTransSeqNo += 1;
245:                            if (ctx.transId > lastActiveTransId) {
246:                                lastActiveTransId = ctx.transId;
247:                            }
248:                        }
249:                    } finally {
250:                        root.unlock();
251:                        ctx.endTransaction();
252:                    }
253:                }
254:            }
255:
256:            /**
257:             * Insert new record in the database
258:             * @param record inserted record
259:             * @exception ObjectAlreadyInsertedException when object is part of some other version history
260:             * @exception TransactionNotStartedException if transaction was not started by this thread using CDatabase.beginTransaction
261:             */
262:            public <T extends CVersion> void insert(T record) {
263:                getWriteTransactionContext().insert(record);
264:            }
265:
266:            /**
267:             * Update the record. 
268:             * @param record updated record
269:             * @return working copy of the specified version
270:             * @exception AmbiguousVersionException when some other version from the same version history was already updated by the current transaction
271:             * @exception TransactionNotStartedException if transaction was not started by this thread using CDatabase.beginTransaction
272:             */
273:            public <T extends CVersion> T update(T record) {
274:                return (T) record.update();
275:            }
276:
277:            /**
278:             * Mark current version as been deleted.
279:             * @param record working copy of the current version or current version itself. In the last case work copy is created
280:             * @exception NotCurrentVersionException is thrown if specified version is not current in the version history
281:             * @exception TransactionNotStartedException if transaction was not started by this thread using CDatabase.beginTransaction
282:             */
283:            public void delete(CVersion record) {
284:                record.delete();
285:            }
286:
287:            /**
288:             * Extract single result from the returned result set.
289:             * It should be applied as pipeline to CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute methods:
290:             * <code>
291:             *    MyClass obj = db.getSingleton(db.find(MyClass.class, "key", value));
292:             * </code>
293:             * @param iterator result set iterator returned by CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute
294:             * methods
295:             * @return selected object if result set contains exactly one object or null if result set is empty
296:             * @exception SingletonException if result set contains more than one element
297:             */
298:            public <T extends CVersion> T getSingleton(Iterator<T> iterator)
299:                    throws SingletonException {
300:                root.sharedLock();
301:                try {
302:                    T obj = null;
303:                    if (iterator.hasNext()) {
304:                        obj = iterator.next();
305:                        if (iterator.hasNext()) {
306:                            throw new SingletonException();
307:                        }
308:                    }
309:                    return obj;
310:                } finally {
311:                    root.unlock();
312:                }
313:            }
314:
315:            /**
316:             * Enumeration specifying version sort order in the result set.
317:             * Sorting is performed by version identifier
318:             */
319:            public enum VersionSortOrder {
320:                ASCENT(1), NONE(0), DESCENT(-1);
321:
322:                int delta;
323:
324:                VersionSortOrder(int delta) {
325:                    this .delta = delta;
326:                }
327:            }
328:
329:            /**
330:             * Convert returned result set to the List collection. 
331:             * It should be applied as pipeline to CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute methods:
332:             * <code>
333:             *    List<MyClass> list = db.toList(db.select(MyClass.class, "price between 100 and 1000 and amount > 100"));
334:             * </code>
335:             * Result set iterator usually selects records in Perst in lazy mode, so execution of query is very 
336:             * fast but fetching each element requires some additional calculations. 
337:             * Using this method you can force loading of all result set elements. So you will know
338:             * precise number of selected records, can access them in any order and without extra overhead.
339:             * The payment for these advantages is increased time of query execution and amount of memory needed
340:             * to hold all selected objects.
341:             * @param iterator result set iterator returned by CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute
342:             * methods
343:             * @return List containing all selected objects
344:             */
345:            public <T extends CVersion> List<T> toList(Iterator<T> iterator) {
346:                return toList(iterator, Integer.MAX_VALUE);
347:            }
348:
349:            /**
350:             * Converted returned result set to List collection with limit for number of fetched records. 
351:             * It should be applied as pipeline to CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute methods:
352:             * <code>
353:             *    List<MyClass> list = db.toList(db.select(MyClass.class, "price between 100 and 1000 and amount > 100"));
354:             * </code>
355:             * Result set iterator usually selects records in Perst in lazy mode, so execution of query is very 
356:             * fast but fetching each element requires some additional calculations. 
357:             * Using this method you can force loading of all result set elements. So you will know
358:             * precise number of selected results, can access them in any order and without extra overhead.
359:             * The payment for these advantages is increased time of query execution and amount of memory needed
360:             * to hold all selected objects.
361:             * @param iterator result set iterator returned by CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute
362:             * methods
363:             * @param limit result list limit
364:             * @return List containing selected objects, if number of selected objects is larger than specified <code>limit</code>, 
365:             * then only first <code>limit</code> of them will be placed in the list and other will be ignored.
366:             */
367:            public <T extends CVersion> List<T> toList(Iterator<T> iterator,
368:                    int limit) {
369:                ArrayList<T> list = new ArrayList();
370:                root.sharedLock();
371:                try {
372:                    while (--limit >= 0 && iterator.hasNext()) {
373:                        list.add(iterator.next());
374:                    }
375:                    return list;
376:                } finally {
377:                    root.unlock();
378:                }
379:            }
380:
381:            /**
382:             * Converted returned result set to array.
383:             * It should be applied as pipeline to CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute methods:
384:             * <code>
385:             *    MyClass[] arr = db.toArray(new MyClass[0], db.select(MyClass.class, "price between 100 and 1000 and amount > 100", 
386:             *                                                         VersionSortOrder.ASCENT));
387:             * </code>
388:             * Result set iterator usually selects records in Perst in lazy mode, so execution of query is very 
389:             * fast but fetching each element requires some additional calculations. 
390:             * Using this method you can force loading of all result set elements. So you will know
391:             * precise number of selected results, can access them in any order and without extra overhead.
392:             * The payment for these advantages is increased time of query execution and amount of memory needed
393:             * to hold all selected objects.
394:             * Specifying sort order allows for example to select the first/last version satisfying search criteria.
395:             * @param arr the array into which the elements of the list are to be stored, 
396:             * if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
397:             * @param iterator result set iterator returned by CDatabase.select/CDatabase.find/CDatabase.getRecords/Query.execute
398:             * methods
399:             * @param order version sort order: ASCENT, DESCENT or NONE. Array elements will be sorted by version identifiers. 
400:             * If sort order is not specified NONE (then records in the result array will be in the same order as returned by the iterator)
401:             * @return an array containing selected objects in the given order
402:             */
403:            public <T extends CVersion> T[] toArray(T[] arr,
404:                    Iterator<T> iterator, VersionSortOrder order) {
405:                ArrayList<T> list = new ArrayList();
406:                root.sharedLock();
407:                try {
408:                    while (iterator.hasNext()) {
409:                        list.add(iterator.next());
410:                    }
411:                } finally {
412:                    root.unlock();
413:                }
414:                arr = list.toArray(arr);
415:                if (order != VersionSortOrder.NONE) {
416:                    final int delta = order.delta;
417:                    Arrays.sort(arr, new Comparator<T>() {
418:                        public int compare(T v1, T v2) {
419:                            return v1.transId < v2.transId ? -delta
420:                                    : v1.transId == v2.transId ? 0 : delta;
421:                        }
422:                    });
423:                }
424:                return arr;
425:            }
426:
427:            /**
428:             * Select current version of records from specified table matching specified criteria
429:             * @param table class corresponding to the table
430:             * @param predicate search predicate
431:             * @return iterator through selected records. This iterator doesn't support remove() method.
432:             * If there are no instances of such class in the database, then empty iterator is returned
433:             * @exception CompileError exception is thrown if predicate is not valid JSQL exception
434:             * @exception JSQLRuntimeException exception is thrown if there is runtime error during query execution
435:             */
436:            public <T extends CVersion> IterableIterator<T> select(Class table,
437:                    String predicate) {
438:                return select(table, predicate, VersionSelector.CURRENT);
439:            }
440:
441:            /**
442:             * Select records from the specified table using version selector
443:             * @param table class corresponding to the table
444:             * @param predicate search predicate
445:             * @param selector version selector
446:             * @return iterator through selected records. This iterator doesn't support remove() method.
447:             * If there are no instances of such class in the database, then empty iterator is returned
448:             * @exception CompileError exception is thrown if predicate is not valid JSQL exception
449:             * @exception JSQLRuntimeException exception is thrown if there is runtime error during query execution
450:             */
451:            public <T extends CVersion> IterableIterator<T> select(Class table,
452:                    String predicate, VersionSelector selector) {
453:                Query q = prepare(table, predicate);
454:                return q.execute(getRecords(table, selector));
455:            }
456:
457:            /**
458:             * Prepare JSQL query. Prepare is needed for queries with parameters. Also
459:             * preparing query can improve speed if query will be executed multiple times
460:             * (using prepare, it is compiled only once).
461:             * To execute prepared query, you should use Query.execute(db.getRecords(TABLE.class)) method
462:             * @param table class corresponding to the table
463:             * @param predicate search predicate
464:             * @return prepared query
465:             * @exception CompileError exception is thrown if predicate is not valid JSQL exception
466:             */
467:            public <T extends CVersion> Query<T> prepare(Class table,
468:                    String predicate) {
469:                return prepare(table, predicate, VersionSelector.CURRENT);
470:            }
471:
472:            /**
473:             * Prepare JSQL query. Prepare is needed for queries with parameters. Also
474:             * preparing query can improve speed if query will be executed multiple times
475:             * (using prepare, it is compiled only once).
476:             * To execute prepared query, you should use Query.execute(db.getRecords(TABLE.class)) method
477:             * @param table class corresponding to the table
478:             * @param predicate search predicate
479:             * @param selector version selector
480:             * @return prepared query
481:             * @exception CompileError exception is thrown if predicate is not valid JSQL exception
482:             */
483:            public <T extends CVersion> Query<T> prepare(Class table,
484:                    String predicate, VersionSelector selector) {
485:                Query q = storage.createQuery();
486:                q.prepare(table, predicate);
487:                TableDescriptor desc = lookupTable(table);
488:                if (desc != null) {
489:                    desc.registerIndices(q, root, selector);
490:                }
491:                return q;
492:            }
493:
494:            /** 
495:             * Get iterator through current version of all table records
496:             * @param table class corresponding to the table
497:             * @return iterator through all table records. If there are no instances of such class in the database, then empty
498:             * iterator is returned
499:             */
500:            public <T extends CVersion> IterableIterator<T> getRecords(
501:                    Class table) {
502:                return getRecords(table, VersionSelector.CURRENT);
503:            }
504:
505:            /** 
506:             * Get iterator through all records of the table using specified version selector
507:             * @param table class corresponding to the table
508:             * @param selector version selector
509:             * @return iterator through all table records. If there are no instances of such class in the database, then empty
510:             * iterator is returned
511:             */
512:            public <T extends CVersion> IterableIterator<T> getRecords(
513:                    Class table, VersionSelector selector) {
514:                root.sharedLock();
515:                try {
516:                    TableDescriptor desc = lookupTable(table);
517:                    IterableIterator<T> iterator = (desc == null) ? new EmptyIterator<T>()
518:                            : new ExtentIterator<T>(desc.iterator(), root,
519:                                    selector);
520:                    return iterator;
521:                } finally {
522:                    root.unlock();
523:                }
524:            }
525:
526:            /**
527:             * Select current version from specified table by key
528:             * @param table class corresponding to the table
529:             * @param field indexed field
530:             * @param key key value
531:             * @return iterator through selected records. This iterator doesn't support remove() method.
532:             * If there are no instances of such class in the database, then empty iterator is returned
533:             * @exception NoSuchIndexException if there is no index for the specified field
534:             */
535:            public <T extends CVersion> IterableIterator<T> find(Class table,
536:                    String field, Key key) throws NoSuchIndexException {
537:                return find(table, field, key, VersionSelector.CURRENT);
538:            }
539:
540:            /**
541:             * Select records from the specified table by key using version selector
542:             * @param table class corresponding to the table
543:             * @param field indexed field
544:             * @param key key value
545:             * @param selector version selector
546:             * @return iterator through selected records. This iterator doesn't support remove() method.
547:             * If there are no instances of such class in the database, then empty iterator is returned
548:             * @exception NoSuchIndexException if there is no index for the specified field
549:             */
550:            public <T extends CVersion> IterableIterator<T> find(Class table,
551:                    String field, Key key, VersionSelector selector)
552:                    throws NoSuchIndexException {
553:                TableDescriptor desc = lookupTable(table);
554:                if (desc == null) {
555:                    return new EmptyIterator<T>();
556:                }
557:                TableDescriptor.IndexDescriptor idesc = desc.findIndex(field);
558:                if (idesc == null) {
559:                    throw new NoSuchIndexException(field);
560:                }
561:                key = idesc.checkKey(key);
562:                return new IndexFilter<T>(idesc.index, root, selector)
563:                        .iterator(key, key, GenericIndex.ASCENT_ORDER);
564:            }
565:
566:            /**
567:             * Select records from the specified table by key range using version selector
568:             * @param table class corresponding to the table
569:             * @param field indexed field
570:             * @param min minimal key value
571:             * @param max maximal key value
572:             * @param selector version selector
573:             * @param order key sort order: GenericIndex.ASCENT_ORDER or GenericIndex.DESCENT_ORDER
574:             * @return iterator through selected records. This iterator doesn't support remove() method.
575:             * If there are no instances of such class in the database, then empty iterator is returned
576:             * @exception NoSuchIndexException if there is no index for the specified field
577:             */
578:            public <T extends CVersion> IterableIterator<T> find(Class table,
579:                    String field, Key min, Key max, VersionSelector selector,
580:                    int order) {
581:                TableDescriptor desc = lookupTable(table);
582:                if (desc == null) {
583:                    return new EmptyIterator<T>();
584:                }
585:                TableDescriptor.IndexDescriptor idesc = desc.findIndex(field);
586:                if (idesc == null) {
587:                    throw new NoSuchIndexException(field);
588:                }
589:                return new IndexFilter<T>(idesc.index, root, selector)
590:                        .iterator(idesc.checkKey(min), idesc.checkKey(max),
591:                                order);
592:            }
593:
594:            /**
595:             * Perform full text search through the current version of all objects in the database
596:             * @param query full text search search query. It should be compliant with Lucene query syntax and may
597:             * refer to the particular fields or to any full text searchable field:
598:             * <ul>
599:             * <li>"name:John" - select document with field "name" containing word "John"</li>
600:             * <li>"title:magic AND author:Clark" - select document with field "title" containing word "magic" and field "author"
601:             *                                     containing word "Clark"</li>
602:             * <li>"atomic nuclear power" - select any document which full text searchable fields contain any of the specified words</li>
603:             * <li>"Class:com.eshop.Player AND description:DivX" - select objects with class com.eshop.Player which description contains word "DivX"</li>
604:             * </ul>
605:             * @param limit search result limit 
606:             * @return array with matched documents
607:             * @exception FullTextSearchQuerySyntaxError if query syntax is invalid
608:             */
609:            public FullTextSearchResult[] fullTextSearch(String query, int limit) {
610:                return fullTextSearch(query, limit, VersionSelector.CURRENT,
611:                        VersionSortOrder.NONE);
612:            }
613:
614:            /**
615:             * Perform full text search using version selector
616:             * @param query full text search search query. It should be compliant with Lucene query syntax and may
617:             * refer to the particular fields or to any full text searchable field:
618:             * <ul>
619:             * <li>"name:John" - select document with field "name" containing word "John"</li>
620:             * <li>"title:magic AND author:Clark" - select document with field "title" containing word "magic" and field "author"
621:             *                                     containing word "Clark"</li>
622:             * <li>"atomic nuclear power" - select any document which full text searchable fields contain any of the specified words</li>
623:             * <li>"Class:com.eshop.Player AND description:DivX" - select objects with class com.eshop.Player which description contains word "DivX"</li>
624:             * </ul>
625:             * @param limit search result limit 
626:             * @param selector version selector
627:             * @param order result set versions sort order (if VersionSortOrder.NONE then order of documents returned by Lucene 
628:             * is preserved - documents are sorted by score)
629:             * @return array with matched documents
630:             * @exception FullTextSearchQuerySyntaxError if query syntax is invalid
631:             */
632:            public FullTextSearchResult[] fullTextSearch(String query,
633:                    int limit, VersionSelector selector, VersionSortOrder order) {
634:                ArrayList<FullTextSearchResult> result = null;
635:                root.sharedLock();
636:                try {
637:                    Hits hits;
638:                    try {
639:                        IndexSearcher searcher = new IndexSearcher(
640:                                getIndexReader());
641:                        if (selector.kind == VersionSelector.Kind.TimeSlice) {
642:                            long from = selector.from != null ? selector.from
643:                                    .getTime() : 0;
644:                            long till = selector.till != null ? selector.till
645:                                    .getTime() : System.currentTimeMillis();
646:                            StringBuilder sb = new StringBuilder("Created:[");
647:                            sb.append(DateTools.timeToString(from,
648:                                    DateTools.Resolution.MINUTE));
649:                            sb.append(" TO ");
650:                            sb.append(DateTools.timeToString(till,
651:                                    DateTools.Resolution.MINUTE));
652:                            sb.append("] AND (");
653:                            sb.append(query);
654:                            sb.append(')');
655:                            query = sb.toString();
656:                        }
657:
658:                        QueryParser parser = new QueryParser("Any", analyzer);
659:                        hits = searcher.search(parser.parse(query));
660:                    } catch (IOException x) {
661:                        throw new IOError(x);
662:                    } catch (ParseException x) {
663:                        throw new FullTextSearchQuerySyntaxError(x);
664:                    }
665:                    result = new ArrayList<FullTextSearchResult>();
666:                    Iterator<FullTextSearchResult> iterator = new FullTextSearchIterator(
667:                            hits, storage, selector);
668:                    while (--limit >= 0 && iterator.hasNext()) {
669:                        result.add(iterator.next());
670:                    }
671:                } finally {
672:                    root.unlock();
673:                }
674:                FullTextSearchResult[] arr = result
675:                        .toArray(new FullTextSearchResult[result.size()]);
676:                if (order != VersionSortOrder.NONE) {
677:                    final int delta = order.delta;
678:                    Arrays.sort(arr, new Comparator<FullTextSearchResult>() {
679:                        public int compare(FullTextSearchResult r1,
680:                                FullTextSearchResult r2) {
681:                            long t1 = r1.getVersion().transId;
682:                            long t2 = r2.getVersion().transId;
683:                            return t1 < t2 ? -delta : t1 == t2 ? 0 : delta;
684:                        }
685:                    });
686:                }
687:                return arr;
688:            }
689:
690:            /**
691:             * Restore full text search index.
692:             * If the full text search index is located in file system directory it may be get out of sync with the Perst database or even be 
693:             * corrupted in case of failure. In this case index can be reconstructed using this method.
694:             */
695:            public synchronized void restoreFullTextIndex() {
696:                root.exclusiveLock();
697:                try {
698:                    try {
699:                        if (indexWriter != null) {
700:                            indexWriter.close();
701:                        }
702:                        if (indexReader != null) {
703:                            indexReader.close();
704:                            indexReader = null;
705:                        }
706:                        indexWriter = new IndexWriter(dir, analyzer, true);
707:                        for (TableDescriptor table : root.tables) {
708:                            for (CVersionHistory<?> vh : table) {
709:                                for (CVersion v : vh) {
710:                                    if (!v.isDeletedVersion()) {
711:                                        Document doc = table.buildDocument(v);
712:                                        if (doc != null) {
713:                                            indexWriter.addDocument(doc);
714:                                        }
715:                                    }
716:                                }
717:                            }
718:                        }
719:                        indexWriter.optimize();
720:                    } catch (IOException x) {
721:                        throw new IOError(x);
722:                    }
723:                } finally {
724:                    root.unlock();
725:                }
726:            }
727:
728:            /**
729:             * Optimize full text index. This method can be periodically called by application (preferably in idle time)
730:             * to optimize full text search index
731:             */
732:            public synchronized void optimizeFullTextIndex() {
733:                root.exclusiveLock();
734:                try {
735:                    try {
736:                        getIndexWriter().optimize();
737:                    } catch (IOException x) {
738:                        throw new IOError(x);
739:                    }
740:                } finally {
741:                    root.unlock();
742:                }
743:            }
744:
745:            /**
746:             * Get storage associated with this database
747:             * @return underlying storage
748:             */
749:            public Storage getStorage() {
750:                return storage;
751:            }
752:
753:            /**
754:             * Get resource used to synchronize access to the database
755:             */
756:            public IResource getResource() {
757:                return root;
758:            }
759:
760:            /**
761:             * Get user data. Continuous uses Perst root objects for its own purposes. 
762:             * But this methods allows application to specify its own root and store in the Perst 
763:             * storage non-versioned objects. 
764:             * @return reference to the persistent object previously stored by setUserData()
765:             */
766:            public IPersistent getUserData() {
767:                root.sharedLock();
768:                try {
769:                    return root.userData;
770:                } finally {
771:                    root.unlock();
772:                }
773:            }
774:
775:            /**
776:             * Set user data. Continuous uses Perst root objects for its own purposes. 
777:             * But this methods allows application to specify its own root and store in the Perst 
778:             * storage non-versioned objects. 
779:             * @param obj reference to the persistent object which will be stored in Perst root object
780:             */
781:            public void setUserData(IPersistent obj) {
782:                root.exclusiveLock();
783:                try {
784:                    root.userData = obj;
785:                    root.modify();
786:                } finally {
787:                    root.unlock();
788:                }
789:            }
790:
791:            /**
792:             * Static instance of the database which can be used by application working with the single database
793:             */
794:            public static CDatabase instance = new CDatabase();
795:
796:            static TransactionContext getTransactionContext() {
797:                return transactionContext.get();
798:            }
799:
800:            static TransactionContext getWriteTransactionContext() {
801:                TransactionContext ctx = transactionContext.get();
802:                if (ctx == null) {
803:                    throw new TransactionNotStartedException();
804:                }
805:                return ctx;
806:            }
807:
808:            void openFullTextIndex(RootObject root, String path) {
809:                boolean create;
810:                if (path == null) {
811:                    // Store Lucene index in Perst database
812:                    create = root.catalogue == null;
813:                    dir = new PerstDirectory(root);
814:                } else {
815:                    File file = new File(path);
816:                    create = !file.exists();
817:                    try {
818:                        dir = FSDirectory.getDirectory(path);
819:                    } catch (IOException x) {
820:                        throw new IOError(x);
821:                    }
822:                }
823:                analyzer = new StandardAnalyzer();
824:                try {
825:                    indexWriter = new IndexWriter(dir, analyzer, create);
826:                } catch (IOException x) {
827:                    throw new IOError(x);
828:                }
829:            }
830:
831:            void excludeFromFullTextIndex(TableDescriptor desc, CVersion v) {
832:                if (desc.isFullTextSearchable()) {
833:                    Term term = new Term("Oid", Integer.toString(v.getOid()));
834:                    try {
835:                        getIndexReader().deleteDocuments(term);
836:                    } catch (IOException x) {
837:                        throw new IOError(x);
838:                    }
839:                }
840:            }
841:
842:            void includeInFullTextIndex(Document doc) {
843:                if (doc != null) {
844:                    try {
845:                        getIndexWriter().addDocument(doc);
846:                    } catch (IOException x) {
847:                        throw new IOError(x);
848:                    }
849:                }
850:            }
851:
852:            synchronized TableDescriptor lookupTable(Class type) {
853:                return typeMap.get(type);
854:            }
855:
856:            synchronized TableDescriptor getTable(Class type) {
857:                TableDescriptor desc = typeMap.get(type);
858:                if (desc == null) {
859:                    desc = new TableDescriptor(storage, type);
860:                    typeMap.put(type, desc);
861:                    root.tables.add(desc);
862:                }
863:                return desc;
864:            }
865:
866:            synchronized IndexWriter getIndexWriter() throws IOException {
867:                if (indexWriter == null) {
868:                    if (indexReader != null) {
869:                        indexReader.close();
870:                        indexReader = null;
871:                    }
872:                    indexWriter = new IndexWriter(dir, analyzer, false);
873:                }
874:                return indexWriter;
875:            }
876:
877:            synchronized IndexReader getIndexReader() throws IOException {
878:                if (indexReader == null) {
879:                    if (indexWriter != null) {
880:                        indexWriter.close();
881:                        indexWriter = null;
882:                    }
883:                    indexReader = IndexReader.open(dir);
884:                }
885:                return indexReader;
886:            }
887:
888:            static ThreadLocal<TransactionContext> transactionContext = new ThreadLocal<TransactionContext>();
889:
890:            Storage storage;
891:            RootObject root;
892:            HashMap<Class, TableDescriptor> typeMap;
893:            IndexWriter indexWriter;
894:            IndexReader indexReader;
895:            StandardAnalyzer analyzer;
896:            Directory dir;
897:            long minTransSeqNo;
898:            long maxTransSeqNo;
899:            long lastActiveTransId;
900:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.