Source Code Cross Referenced for Store.java in  » JMX » je » com » sleepycat » persist » impl » 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 » JMX » je » com.sleepycat.persist.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*-
0002:         * See the file LICENSE for redistribution information.
0003:         *
0004:         * Copyright (c) 2002,2008 Oracle.  All rights reserved.
0005:         *
0006:         * $Id: Store.java,v 1.20.2.8 2008/01/07 15:14:20 cwl Exp $
0007:         */
0008:
0009:        package com.sleepycat.persist.impl;
0010:
0011:        import java.util.ArrayList;
0012:        import java.util.Comparator;
0013:        import java.util.HashMap;
0014:        import java.util.HashSet;
0015:        import java.util.IdentityHashMap;
0016:        import java.util.List;
0017:        import java.util.Map;
0018:        import java.util.Set;
0019:        import java.util.WeakHashMap;
0020:
0021:        import com.sleepycat.bind.EntityBinding;
0022:        import com.sleepycat.bind.tuple.StringBinding;
0023:        import com.sleepycat.compat.DbCompat;
0024:        import com.sleepycat.je.Cursor;
0025:        import com.sleepycat.je.CursorConfig;
0026:        import com.sleepycat.je.Database;
0027:        import com.sleepycat.je.DatabaseConfig;
0028:        import com.sleepycat.je.DatabaseEntry;
0029:        import com.sleepycat.je.DatabaseException;
0030:        import com.sleepycat.je.DatabaseNotFoundException;
0031:        import com.sleepycat.je.Environment;
0032:        import com.sleepycat.je.ForeignKeyDeleteAction;
0033:        import com.sleepycat.je.LockMode;
0034:        import com.sleepycat.je.OperationStatus;
0035:        import com.sleepycat.je.SecondaryConfig;
0036:        import com.sleepycat.je.SecondaryDatabase;
0037:        import com.sleepycat.je.Sequence;
0038:        import com.sleepycat.je.SequenceConfig;
0039:        import com.sleepycat.je.Transaction;
0040:        import com.sleepycat.persist.PrimaryIndex;
0041:        import com.sleepycat.persist.SecondaryIndex;
0042:        import com.sleepycat.persist.StoreConfig;
0043:        import com.sleepycat.persist.evolve.Converter;
0044:        import com.sleepycat.persist.evolve.EvolveConfig;
0045:        import com.sleepycat.persist.evolve.EvolveEvent;
0046:        import com.sleepycat.persist.evolve.EvolveInternal;
0047:        import com.sleepycat.persist.evolve.EvolveListener;
0048:        import com.sleepycat.persist.evolve.EvolveStats;
0049:        import com.sleepycat.persist.evolve.Mutations;
0050:        import com.sleepycat.persist.model.ClassMetadata;
0051:        import com.sleepycat.persist.model.DeleteAction;
0052:        import com.sleepycat.persist.model.EntityMetadata;
0053:        import com.sleepycat.persist.model.EntityModel;
0054:        import com.sleepycat.persist.model.FieldMetadata;
0055:        import com.sleepycat.persist.model.ModelInternal;
0056:        import com.sleepycat.persist.model.PrimaryKeyMetadata;
0057:        import com.sleepycat.persist.model.Relationship;
0058:        import com.sleepycat.persist.model.SecondaryKeyMetadata;
0059:        import com.sleepycat.persist.raw.RawObject;
0060:
0061:        /**
0062:         * Base implementation for EntityStore and  RawStore.  The methods here
0063:         * correspond directly to those in EntityStore; see EntityStore documentation
0064:         * for details.
0065:         *
0066:         * @author Mark Hayes
0067:         */
0068:        public class Store {
0069:
0070:            private static final char NAME_SEPARATOR = '#';
0071:            private static final String NAME_PREFIX = "persist"
0072:                    + NAME_SEPARATOR;
0073:            private static final String DB_NAME_PREFIX = "com.sleepycat.persist.";
0074:            private static final String CATALOG_DB = DB_NAME_PREFIX + "formats";
0075:            private static final String SEQUENCE_DB = DB_NAME_PREFIX
0076:                    + "sequences";
0077:
0078:            private static Map<Environment, Map<String, PersistCatalog>> catalogPool = new WeakHashMap<Environment, Map<String, PersistCatalog>>();
0079:
0080:            /* For unit testing. */
0081:            private static SyncHook syncHook;
0082:
0083:            private Environment env;
0084:            private boolean rawAccess;
0085:            private PersistCatalog catalog;
0086:            private EntityModel model;
0087:            private Mutations mutations;
0088:            private StoreConfig storeConfig;
0089:            private String storeName;
0090:            private String storePrefix;
0091:            private Map<String, PrimaryIndex> priIndexMap;
0092:            private Map<String, SecondaryIndex> secIndexMap;
0093:            private Map<String, DatabaseConfig> priConfigMap;
0094:            private Map<String, SecondaryConfig> secConfigMap;
0095:            private Map<String, PersistKeyBinding> keyBindingMap;
0096:            private Map<String, Sequence> sequenceMap;
0097:            private Map<String, SequenceConfig> sequenceConfigMap;
0098:            private Database sequenceDb;
0099:            private IdentityHashMap<Database, Object> deferredWriteDatabases;
0100:            private Map<String, Set<String>> inverseRelatedEntityMap;
0101:
0102:            public Store(Environment env, String storeName, StoreConfig config,
0103:                    boolean rawAccess) throws DatabaseException {
0104:
0105:                this .env = env;
0106:                this .storeName = storeName;
0107:                this .rawAccess = rawAccess;
0108:
0109:                if (env == null || storeName == null) {
0110:                    throw new NullPointerException(
0111:                            "env and storeName parameters must not be null");
0112:                }
0113:                if (config != null) {
0114:                    model = config.getModel();
0115:                    mutations = config.getMutations();
0116:                }
0117:                if (config == null) {
0118:                    storeConfig = StoreConfig.DEFAULT;
0119:                } else {
0120:                    storeConfig = config.cloneConfig();
0121:                }
0122:
0123:                storePrefix = NAME_PREFIX + storeName + NAME_SEPARATOR;
0124:                priIndexMap = new HashMap<String, PrimaryIndex>();
0125:                secIndexMap = new HashMap<String, SecondaryIndex>();
0126:                priConfigMap = new HashMap<String, DatabaseConfig>();
0127:                secConfigMap = new HashMap<String, SecondaryConfig>();
0128:                keyBindingMap = new HashMap<String, PersistKeyBinding>();
0129:                sequenceMap = new HashMap<String, Sequence>();
0130:                sequenceConfigMap = new HashMap<String, SequenceConfig>();
0131:                deferredWriteDatabases = new IdentityHashMap<Database, Object>();
0132:
0133:                if (rawAccess) {
0134:                    /* Open a read-only catalog that uses the stored model. */
0135:                    if (model != null) {
0136:                        throw new IllegalArgumentException(
0137:                                "A model may not be specified when opening a RawStore");
0138:                    }
0139:                    DatabaseConfig dbConfig = new DatabaseConfig();
0140:                    dbConfig.setReadOnly(true);
0141:                    dbConfig.setTransactional(storeConfig.getTransactional());
0142:                    catalog = new PersistCatalog(null, env, storePrefix,
0143:                            storePrefix + CATALOG_DB, dbConfig, model,
0144:                            mutations, rawAccess, this );
0145:                } else {
0146:                    /* Open the shared catalog that uses the current model. */
0147:                    synchronized (catalogPool) {
0148:                        Map<String, PersistCatalog> catalogMap = catalogPool
0149:                                .get(env);
0150:                        if (catalogMap == null) {
0151:                            catalogMap = new HashMap<String, PersistCatalog>();
0152:                            catalogPool.put(env, catalogMap);
0153:                        }
0154:                        catalog = catalogMap.get(storeName);
0155:                        if (catalog != null) {
0156:                            catalog.openExisting();
0157:                        } else {
0158:                            Transaction txn = null;
0159:                            if (storeConfig.getTransactional()
0160:                                    && env.getThreadTransaction() == null) {
0161:                                txn = env.beginTransaction(null, null);
0162:                            }
0163:                            boolean success = false;
0164:                            try {
0165:                                DatabaseConfig dbConfig = new DatabaseConfig();
0166:                                dbConfig.setAllowCreate(storeConfig
0167:                                        .getAllowCreate());
0168:                                dbConfig.setReadOnly(storeConfig.getReadOnly());
0169:                                dbConfig.setTransactional(storeConfig
0170:                                        .getTransactional());
0171:                                catalog = new PersistCatalog(txn, env,
0172:                                        storePrefix, storePrefix + CATALOG_DB,
0173:                                        dbConfig, model, mutations, rawAccess,
0174:                                        this );
0175:                                catalogMap.put(storeName, catalog);
0176:                                success = true;
0177:                            } finally {
0178:                                if (txn != null) {
0179:                                    if (success) {
0180:                                        txn.commit();
0181:                                    } else {
0182:                                        txn.abort();
0183:                                    }
0184:                                }
0185:                            }
0186:                        }
0187:                    }
0188:                }
0189:
0190:                /* Get the merged mutations from the catalog. */
0191:                mutations = catalog.getMutations();
0192:
0193:                /*
0194:                 * If there is no model parameter, use the default or stored model
0195:                 * obtained from the catalog.
0196:                 */
0197:                model = catalog.getResolvedModel();
0198:
0199:                /*
0200:                 * Give the model a reference to the catalog to fully initialize the
0201:                 * model.  Only then may we initialize the Converter mutations, which
0202:                 * themselves may call model methods and expect the model to be fully
0203:                 * initialized.
0204:                 */
0205:                ModelInternal.setCatalog(model, catalog);
0206:                for (Converter converter : mutations.getConverters()) {
0207:                    converter.getConversion().initialize(model);
0208:                }
0209:
0210:                /*
0211:                 * For each existing entity with a relatedEntity reference, create an
0212:                 * inverse map (back pointer) from the class named in the relatedEntity
0213:                 * to the class containing the secondary key.  This is used to open the
0214:                 * class containing the secondary key whenever we open the
0215:                 * relatedEntity class, to configure foreign key constraints. Note that
0216:                 * we do not need to update this map as new primary indexes are
0217:                 * created, because opening the new index will setup the foreign key
0218:                 * constraints. [#15358]
0219:                 */
0220:                inverseRelatedEntityMap = new HashMap<String, Set<String>>();
0221:                List<Format> entityFormats = new ArrayList<Format>();
0222:                catalog.getEntityFormats(entityFormats);
0223:                for (Format entityFormat : entityFormats) {
0224:                    EntityMetadata entityMeta = entityFormat
0225:                            .getEntityMetadata();
0226:                    for (SecondaryKeyMetadata secKeyMeta : entityMeta
0227:                            .getSecondaryKeys().values()) {
0228:                        String relatedClsName = secKeyMeta.getRelatedEntity();
0229:                        if (relatedClsName != null) {
0230:                            Set<String> inverseClassNames = inverseRelatedEntityMap
0231:                                    .get(relatedClsName);
0232:                            if (inverseClassNames == null) {
0233:                                inverseClassNames = new HashSet<String>();
0234:                                inverseRelatedEntityMap.put(relatedClsName,
0235:                                        inverseClassNames);
0236:                            }
0237:                            inverseClassNames.add(entityMeta.getClassName());
0238:                        }
0239:                    }
0240:                }
0241:            }
0242:
0243:            public Environment getEnvironment() {
0244:                return env;
0245:            }
0246:
0247:            public StoreConfig getConfig() {
0248:                return storeConfig.cloneConfig();
0249:            }
0250:
0251:            public String getStoreName() {
0252:                return storeName;
0253:            }
0254:
0255:            public void dumpCatalog() {
0256:                catalog.dump();
0257:            }
0258:
0259:            public static Set<String> getStoreNames(Environment env)
0260:                    throws DatabaseException {
0261:
0262:                Set<String> set = new HashSet<String>();
0263:                for (Object o : env.getDatabaseNames()) {
0264:                    String s = (String) o;
0265:                    if (s.startsWith(NAME_PREFIX)) {
0266:                        int start = NAME_PREFIX.length();
0267:                        int end = s.indexOf(NAME_SEPARATOR, start);
0268:                        set.add(s.substring(start, end));
0269:                    }
0270:                }
0271:                return set;
0272:            }
0273:
0274:            public EntityModel getModel() {
0275:                return model;
0276:            }
0277:
0278:            public Mutations getMutations() {
0279:                return mutations;
0280:            }
0281:
0282:            /**
0283:             * A getPrimaryIndex with extra parameters for opening a raw store.
0284:             * primaryKeyClass and entityClass are used for generic typing; for a raw
0285:             * store, these should always be Object.class and RawObject.class.
0286:             * primaryKeyClassName is used for consistency checking and should be null
0287:             * for a raw store only.  entityClassName is used to identify the store and
0288:             * may not be null.
0289:             */
0290:            public synchronized <PK, E> PrimaryIndex<PK, E> getPrimaryIndex(
0291:                    Class<PK> primaryKeyClass, String primaryKeyClassName,
0292:                    Class<E> entityClass, String entityClassName)
0293:                    throws DatabaseException {
0294:
0295:                assert (rawAccess && entityClass == RawObject.class)
0296:                        || (!rawAccess && entityClass != RawObject.class);
0297:                assert (rawAccess && primaryKeyClassName == null)
0298:                        || (!rawAccess && primaryKeyClassName != null);
0299:
0300:                checkOpen();
0301:
0302:                PrimaryIndex<PK, E> priIndex = priIndexMap.get(entityClassName);
0303:                if (priIndex == null) {
0304:
0305:                    /* Check metadata. */
0306:                    EntityMetadata entityMeta = checkEntityClass(entityClassName);
0307:                    PrimaryKeyMetadata priKeyMeta = entityMeta.getPrimaryKey();
0308:                    if (primaryKeyClassName == null) {
0309:                        primaryKeyClassName = priKeyMeta.getClassName();
0310:                    } else {
0311:                        String expectClsName = SimpleCatalog
0312:                                .keyClassName(priKeyMeta.getClassName());
0313:                        if (!primaryKeyClassName.equals(expectClsName)) {
0314:                            throw new IllegalArgumentException(
0315:                                    "Wrong primary key class: "
0316:                                            + primaryKeyClassName
0317:                                            + " Correct class is: "
0318:                                            + expectClsName);
0319:                        }
0320:                    }
0321:
0322:                    /* Create bindings. */
0323:                    PersistEntityBinding entityBinding = new PersistEntityBinding(
0324:                            catalog, entityClassName, rawAccess);
0325:                    PersistKeyBinding keyBinding = getKeyBinding(primaryKeyClassName);
0326:
0327:                    /* If not read-only, get the primary key sequence. */
0328:                    String seqName = priKeyMeta.getSequenceName();
0329:                    if (!storeConfig.getReadOnly() && seqName != null) {
0330:                        entityBinding.keyAssigner = new PersistKeyAssigner(
0331:                                keyBinding, entityBinding, getSequence(seqName));
0332:                    }
0333:
0334:                    /*
0335:                     * Use a single transaction for opening the primary DB and its
0336:                     * secondaries.  If opening any secondary fails, abort the
0337:                     * transaction and undo the changes to the state of the store.
0338:                     * Also support undo if the store is non-transactional.
0339:                     */
0340:                    Transaction txn = null;
0341:                    DatabaseConfig dbConfig = getPrimaryConfig(entityMeta);
0342:                    if (dbConfig.getTransactional()
0343:                            && env.getThreadTransaction() == null) {
0344:                        txn = env.beginTransaction(null, null);
0345:                    }
0346:                    PrimaryOpenState priOpenState = new PrimaryOpenState(
0347:                            entityClassName);
0348:                    boolean success = false;
0349:                    try {
0350:
0351:                        /* Open the primary database. */
0352:                        String dbName = storePrefix + entityClassName;
0353:                        Database db = env.openDatabase(txn, dbName, dbConfig);
0354:                        priOpenState.addDatabase(db);
0355:
0356:                        /* Create index object. */
0357:                        priIndex = new PrimaryIndex(db, primaryKeyClass,
0358:                                keyBinding, entityClass, entityBinding);
0359:
0360:                        /* Update index and database maps. */
0361:                        priIndexMap.put(entityClassName, priIndex);
0362:                        if (DbCompat.getDeferredWrite(dbConfig)) {
0363:                            deferredWriteDatabases.put(db, null);
0364:                        }
0365:
0366:                        /* If not read-only, open all associated secondaries. */
0367:                        if (!dbConfig.getReadOnly()) {
0368:                            openSecondaryIndexes(txn, entityMeta, priOpenState);
0369:
0370:                            /*
0371:                             * To enable foreign key contratints, also open all primary
0372:                             * indexes referring to this class via a relatedEntity
0373:                             * property in another entity. [#15358]
0374:                             */
0375:                            Set<String> inverseClassNames = inverseRelatedEntityMap
0376:                                    .get(entityClassName);
0377:                            if (inverseClassNames != null) {
0378:                                for (String relatedClsName : inverseClassNames) {
0379:                                    getRelatedIndex(relatedClsName);
0380:                                }
0381:                            }
0382:                        }
0383:                        success = true;
0384:                    } finally {
0385:                        if (success) {
0386:                            if (txn != null) {
0387:                                txn.commit();
0388:                            }
0389:                        } else {
0390:                            if (txn != null) {
0391:                                txn.abort();
0392:                            } else {
0393:                                priOpenState.closeDatabases();
0394:                            }
0395:                            priOpenState.undoState();
0396:                        }
0397:                    }
0398:                }
0399:                return priIndex;
0400:            }
0401:
0402:            /**
0403:             * Holds state information about opening a primary index and its secondary
0404:             * indexes.  Used to undo the state of this object if the transaction
0405:             * opening the primary and secondaries aborts.  Also used to close all
0406:             * databases opened during this process for a non-transactional store.
0407:             */
0408:            private class PrimaryOpenState {
0409:
0410:                private String entityClassName;
0411:                private IdentityHashMap<Database, Object> databases;
0412:                private Set<String> secNames;
0413:
0414:                PrimaryOpenState(String entityClassName) {
0415:                    this .entityClassName = entityClassName;
0416:                    databases = new IdentityHashMap<Database, Object>();
0417:                    secNames = new HashSet<String>();
0418:                }
0419:
0420:                /**
0421:                 * Save a database that was opening during this operation.
0422:                 */
0423:                void addDatabase(Database db) {
0424:                    databases.put(db, null);
0425:                }
0426:
0427:                /**
0428:                 * Save the name of a secondary index that was opening during this
0429:                 * operation.
0430:                 */
0431:                void addSecondaryName(String secName) {
0432:                    secNames.add(secName);
0433:                }
0434:
0435:                /**
0436:                 * Close any databases opened during this operation when it fails.
0437:                 * This method should be called if a non-transactional operation fails,
0438:                 * since we cannot rely on the transaction abort to cleanup any
0439:                 * databases that were opened.
0440:                 */
0441:                void closeDatabases() {
0442:                    for (Database db : databases.keySet()) {
0443:                        try {
0444:                            db.close();
0445:                        } catch (Exception ignored) {
0446:                        }
0447:                    }
0448:                }
0449:
0450:                /**
0451:                 * Reset all state information when this operation fails.  This method
0452:                 * should be called for both transactional and non-transsactional
0453:                 * operation.
0454:                 */
0455:                void undoState() {
0456:                    priIndexMap.remove(entityClassName);
0457:                    for (String secName : secNames) {
0458:                        secIndexMap.remove(secName);
0459:                    }
0460:                    for (Database db : databases.keySet()) {
0461:                        deferredWriteDatabases.remove(db);
0462:                    }
0463:                }
0464:            }
0465:
0466:            /**
0467:             * Opens a primary index related via a foreign key (relatedEntity).
0468:             * Related indexes are not opened in the same transaction used by the
0469:             * caller to open a primary or secondary.  It is OK to leave the related
0470:             * index open when the caller's transaction aborts.  It is only important
0471:             * to open a primary and its secondaries atomically.
0472:             */
0473:            private PrimaryIndex getRelatedIndex(String relatedClsName)
0474:                    throws DatabaseException {
0475:
0476:                PrimaryIndex relatedIndex = priIndexMap.get(relatedClsName);
0477:                if (relatedIndex == null) {
0478:                    EntityMetadata relatedEntityMeta = checkEntityClass(relatedClsName);
0479:                    Class relatedKeyCls;
0480:                    String relatedKeyClsName;
0481:                    Class relatedCls;
0482:                    if (rawAccess) {
0483:                        relatedCls = RawObject.class;
0484:                        relatedKeyCls = Object.class;
0485:                        relatedKeyClsName = null;
0486:                    } else {
0487:                        try {
0488:                            relatedCls = EntityModel
0489:                                    .classForName(relatedClsName);
0490:                        } catch (ClassNotFoundException e) {
0491:                            throw new IllegalArgumentException(
0492:                                    "Related entity class not found: "
0493:                                            + relatedClsName);
0494:                        }
0495:                        relatedKeyClsName = SimpleCatalog
0496:                                .keyClassName(relatedEntityMeta.getPrimaryKey()
0497:                                        .getClassName());
0498:                        relatedKeyCls = SimpleCatalog
0499:                                .keyClassForName(relatedKeyClsName);
0500:                    }
0501:
0502:                    /*
0503:                     * Cycles are prevented here by adding primary indexes to the
0504:                     * priIndexMap as soon as they are created, before opening related
0505:                     * indexes.
0506:                     */
0507:                    relatedIndex = getPrimaryIndex(relatedKeyCls,
0508:                            relatedKeyClsName, relatedCls, relatedClsName);
0509:                }
0510:                return relatedIndex;
0511:            }
0512:
0513:            /**
0514:             * A getSecondaryIndex with extra parameters for opening a raw store.
0515:             * keyClassName is used for consistency checking and should be null for a
0516:             * raw store only.
0517:             */
0518:            public synchronized <SK, PK, E1, E2 extends E1> SecondaryIndex<SK, PK, E2> getSecondaryIndex(
0519:                    PrimaryIndex<PK, E1> primaryIndex, Class<E2> entityClass,
0520:                    String entityClassName, Class<SK> keyClass,
0521:                    String keyClassName, String keyName)
0522:                    throws DatabaseException {
0523:
0524:                assert (rawAccess && keyClassName == null)
0525:                        || (!rawAccess && keyClassName != null);
0526:
0527:                checkOpen();
0528:
0529:                EntityMetadata entityMeta = null;
0530:                SecondaryKeyMetadata secKeyMeta = null;
0531:
0532:                /* Validate the subclass for a subclass index. */
0533:                if (entityClass != primaryIndex.getEntityClass()) {
0534:                    entityMeta = model.getEntityMetadata(entityClassName);
0535:                    assert entityMeta != null;
0536:                    secKeyMeta = checkSecKey(entityMeta, keyName);
0537:                    String subclassName = entityClass.getName();
0538:                    String declaringClassName = secKeyMeta
0539:                            .getDeclaringClassName();
0540:                    if (!subclassName.equals(declaringClassName)) {
0541:                        throw new IllegalArgumentException("Key for subclass "
0542:                                + subclassName
0543:                                + " is declared in a different class: "
0544:                                + makeSecName(declaringClassName, keyName));
0545:                    }
0546:                }
0547:
0548:                /*
0549:                 * Even though the primary is already open, we can't assume the
0550:                 * secondary is open because we don't automatically open all
0551:                 * secondaries when the primary is read-only.  Use auto-commit (a null
0552:                 * transaction) since we're opening only one database.
0553:                 */
0554:                String secName = makeSecName(entityClassName, keyName);
0555:                SecondaryIndex<SK, PK, E2> secIndex = secIndexMap.get(secName);
0556:                if (secIndex == null) {
0557:                    if (entityMeta == null) {
0558:                        entityMeta = model.getEntityMetadata(entityClassName);
0559:                        assert entityMeta != null;
0560:                    }
0561:                    if (secKeyMeta == null) {
0562:                        secKeyMeta = checkSecKey(entityMeta, keyName);
0563:                    }
0564:
0565:                    /* Check metadata. */
0566:                    if (keyClassName == null) {
0567:                        keyClassName = getSecKeyClass(secKeyMeta);
0568:                    } else {
0569:                        String expectClsName = getSecKeyClass(secKeyMeta);
0570:                        if (!keyClassName.equals(expectClsName)) {
0571:                            throw new IllegalArgumentException(
0572:                                    "Wrong secondary key class: "
0573:                                            + keyClassName
0574:                                            + " Correct class is: "
0575:                                            + expectClsName);
0576:                        }
0577:                    }
0578:
0579:                    secIndex = openSecondaryIndex(null, primaryIndex,
0580:                            entityClass, entityMeta, keyClass, keyClassName,
0581:                            secKeyMeta, secName, false /*doNotCreate*/, null /*priOpenState*/);
0582:                }
0583:                return secIndex;
0584:            }
0585:
0586:            /**
0587:             * Opens any secondary indexes defined in the given entity metadata that
0588:             * are not already open.  This method is called when a new entity subclass
0589:             * is encountered when an instance of that class is stored, and the
0590:             * EntityStore.getSubclassIndex has not been previously called for that
0591:             * class. [#15247]
0592:             */
0593:            synchronized void openSecondaryIndexes(Transaction txn,
0594:                    EntityMetadata entityMeta, PrimaryOpenState priOpenState)
0595:                    throws DatabaseException {
0596:
0597:                String entityClassName = entityMeta.getClassName();
0598:                PrimaryIndex<Object, Object> priIndex = priIndexMap
0599:                        .get(entityClassName);
0600:                assert priIndex != null;
0601:                Class<Object> entityClass = priIndex.getEntityClass();
0602:
0603:                for (SecondaryKeyMetadata secKeyMeta : entityMeta
0604:                        .getSecondaryKeys().values()) {
0605:                    String keyName = secKeyMeta.getKeyName();
0606:                    String secName = makeSecName(entityClassName, keyName);
0607:                    SecondaryIndex<Object, Object, Object> secIndex = secIndexMap
0608:                            .get(secName);
0609:                    if (secIndex == null) {
0610:                        String keyClassName = getSecKeyClass(secKeyMeta);
0611:                        /* RawMode: should not require class. */
0612:                        Class keyClass = SimpleCatalog
0613:                                .keyClassForName(keyClassName);
0614:                        openSecondaryIndex(
0615:                                txn,
0616:                                priIndex,
0617:                                entityClass,
0618:                                entityMeta,
0619:                                keyClass,
0620:                                keyClassName,
0621:                                secKeyMeta,
0622:                                makeSecName(entityClassName, secKeyMeta
0623:                                        .getKeyName()),
0624:                                storeConfig.getSecondaryBulkLoad() /*doNotCreate*/,
0625:                                priOpenState);
0626:                    }
0627:                }
0628:            }
0629:
0630:            /**
0631:             * Opens a secondary index with a given transaction and adds it to the
0632:             * secIndexMap.  We assume that the index is not already open.
0633:             */
0634:            private <SK, PK, E1, E2 extends E1> SecondaryIndex<SK, PK, E2> openSecondaryIndex(
0635:                    Transaction txn, PrimaryIndex<PK, E1> primaryIndex,
0636:                    Class<E2> entityClass, EntityMetadata entityMeta,
0637:                    Class<SK> keyClass, String keyClassName,
0638:                    SecondaryKeyMetadata secKeyMeta, String secName,
0639:                    boolean doNotCreate, PrimaryOpenState priOpenState)
0640:                    throws DatabaseException {
0641:
0642:                assert !secIndexMap.containsKey(secName);
0643:                String dbName = storePrefix + secName;
0644:                SecondaryConfig config = getSecondaryConfig(secName,
0645:                        entityMeta, keyClassName, secKeyMeta);
0646:                Database priDb = primaryIndex.getDatabase();
0647:                DatabaseConfig priConfig = priDb.getConfig();
0648:
0649:                String relatedClsName = secKeyMeta.getRelatedEntity();
0650:                if (relatedClsName != null) {
0651:                    PrimaryIndex relatedIndex = getRelatedIndex(relatedClsName);
0652:                    config.setForeignKeyDatabase(relatedIndex.getDatabase());
0653:                }
0654:
0655:                if (config.getTransactional() != priConfig.getTransactional()
0656:                        || DbCompat.getDeferredWrite(config) != DbCompat
0657:                                .getDeferredWrite(priConfig)
0658:                        || config.getReadOnly() != priConfig.getReadOnly()) {
0659:                    throw new IllegalArgumentException(
0660:                            "One of these properties was changed to be inconsistent"
0661:                                    + " with the associated primary database: "
0662:                                    + " Transactional, DeferredWrite, ReadOnly");
0663:                }
0664:
0665:                PersistKeyBinding keyBinding = getKeyBinding(keyClassName);
0666:
0667:                /*
0668:                 * doNotCreate is true when StoreConfig.getSecondaryBulkLoad is true
0669:                 * and we are opening a secondary as a side effect of opening a
0670:                 * primary, i.e., getSecondaryIndex is not being called.  If
0671:                 * doNotCreate is true and the database does not exist, we silently
0672:                 * ignore the DatabaseNotFoundException and return null.  When
0673:                 * getSecondaryIndex is subsequently called, the secondary database
0674:                 * will be created and populated from the primary -- a bulk load.
0675:                 */
0676:                SecondaryDatabase db;
0677:                boolean saveAllowCreate = config.getAllowCreate();
0678:                try {
0679:                    if (doNotCreate) {
0680:                        config.setAllowCreate(false);
0681:                    }
0682:                    db = env.openSecondaryDatabase(txn, dbName, priDb, config);
0683:                } catch (DatabaseNotFoundException e) {
0684:                    if (doNotCreate) {
0685:                        return null;
0686:                    } else {
0687:                        throw e;
0688:                    }
0689:                } finally {
0690:                    if (doNotCreate) {
0691:                        config.setAllowCreate(saveAllowCreate);
0692:                    }
0693:                }
0694:                SecondaryIndex<SK, PK, E2> secIndex = new SecondaryIndex(db,
0695:                        null, primaryIndex, keyClass, keyBinding);
0696:
0697:                /* Update index and database maps. */
0698:                secIndexMap.put(secName, secIndex);
0699:                if (DbCompat.getDeferredWrite(config)) {
0700:                    deferredWriteDatabases.put(db, null);
0701:                }
0702:                if (priOpenState != null) {
0703:                    priOpenState.addDatabase(db);
0704:                    priOpenState.addSecondaryName(secName);
0705:                }
0706:                return secIndex;
0707:            }
0708:
0709:            public void sync() throws DatabaseException {
0710:
0711:                List<Database> dbs = new ArrayList<Database>();
0712:                synchronized (this ) {
0713:                    dbs.addAll(deferredWriteDatabases.keySet());
0714:                }
0715:                int nDbs = dbs.size();
0716:                if (nDbs > 0) {
0717:                    for (int i = 0; i < nDbs; i += 1) {
0718:                        Database db = dbs.get(i);
0719:                        boolean flushLog = (i == nDbs - 1);
0720:                        DbCompat.syncDeferredWrite(db, flushLog);
0721:                        /* Call hook for unit testing. */
0722:                        if (syncHook != null) {
0723:                            syncHook.onSync(db, flushLog);
0724:                        }
0725:                    }
0726:                }
0727:            }
0728:
0729:            public void truncateClass(Class entityClass)
0730:                    throws DatabaseException {
0731:
0732:                truncateClass(null, entityClass);
0733:            }
0734:
0735:            public synchronized void truncateClass(Transaction txn,
0736:                    Class entityClass) throws DatabaseException {
0737:
0738:                checkOpen();
0739:
0740:                /* Close primary and secondary databases. */
0741:                closeClass(entityClass);
0742:
0743:                String clsName = entityClass.getName();
0744:                EntityMetadata entityMeta = checkEntityClass(clsName);
0745:
0746:                /*
0747:                 * Truncate the primary first and let any exceptions propogate
0748:                 * upwards.  Then truncate each secondary, only throwing the first
0749:                 * exception.
0750:                 */
0751:                String dbName = storePrefix + clsName;
0752:                boolean primaryExists = true;
0753:                try {
0754:                    env.truncateDatabase(txn, dbName, false);
0755:                } catch (DatabaseNotFoundException ignored) {
0756:                    primaryExists = false;
0757:                }
0758:                if (primaryExists) {
0759:                    DatabaseException firstException = null;
0760:                    for (SecondaryKeyMetadata keyMeta : entityMeta
0761:                            .getSecondaryKeys().values()) {
0762:                        try {
0763:                            env.truncateDatabase(txn,
0764:                                    storePrefix
0765:                                            + makeSecName(clsName, keyMeta
0766:                                                    .getKeyName()), false);
0767:                        } catch (DatabaseNotFoundException ignored) {
0768:                            /* Ignore secondaries that do not exist. */
0769:                        } catch (DatabaseException e) {
0770:                            if (firstException == null) {
0771:                                firstException = e;
0772:                            }
0773:                        }
0774:                    }
0775:                    if (firstException != null) {
0776:                        throw firstException;
0777:                    }
0778:                }
0779:            }
0780:
0781:            public synchronized void closeClass(Class entityClass)
0782:                    throws DatabaseException {
0783:
0784:                checkOpen();
0785:                String clsName = entityClass.getName();
0786:                EntityMetadata entityMeta = checkEntityClass(clsName);
0787:
0788:                PrimaryIndex priIndex = priIndexMap.get(clsName);
0789:                if (priIndex != null) {
0790:                    /* Close the secondaries first. */
0791:                    DatabaseException firstException = null;
0792:                    for (SecondaryKeyMetadata keyMeta : entityMeta
0793:                            .getSecondaryKeys().values()) {
0794:
0795:                        String secName = makeSecName(clsName, keyMeta
0796:                                .getKeyName());
0797:                        SecondaryIndex secIndex = secIndexMap.get(secName);
0798:                        if (secIndex != null) {
0799:                            Database db = secIndex.getDatabase();
0800:                            firstException = closeDb(db, firstException);
0801:                            firstException = closeDb(
0802:                                    secIndex.getKeysDatabase(), firstException);
0803:                            secIndexMap.remove(secName);
0804:                            deferredWriteDatabases.remove(db);
0805:                        }
0806:                    }
0807:                    /* Close the primary last. */
0808:                    Database db = priIndex.getDatabase();
0809:                    firstException = closeDb(db, firstException);
0810:                    priIndexMap.remove(clsName);
0811:                    deferredWriteDatabases.remove(db);
0812:
0813:                    /* Throw the first exception encountered. */
0814:                    if (firstException != null) {
0815:                        throw firstException;
0816:                    }
0817:                }
0818:            }
0819:
0820:            public synchronized void close() throws DatabaseException {
0821:
0822:                checkOpen();
0823:                DatabaseException firstException = null;
0824:                try {
0825:                    if (rawAccess) {
0826:                        boolean allClosed = catalog.close();
0827:                        assert allClosed;
0828:                    } else {
0829:                        synchronized (catalogPool) {
0830:                            Map<String, PersistCatalog> catalogMap = catalogPool
0831:                                    .get(env);
0832:                            assert catalogMap != null;
0833:                            if (catalog.close()) {
0834:                                /* Remove when the reference count goes to zero. */
0835:                                catalogMap.remove(storeName);
0836:                            }
0837:                        }
0838:                    }
0839:                    catalog = null;
0840:                } catch (DatabaseException e) {
0841:                    if (firstException == null) {
0842:                        firstException = e;
0843:                    }
0844:                }
0845:                firstException = closeDb(sequenceDb, firstException);
0846:                for (SecondaryIndex index : secIndexMap.values()) {
0847:                    firstException = closeDb(index.getDatabase(),
0848:                            firstException);
0849:                    firstException = closeDb(index.getKeysDatabase(),
0850:                            firstException);
0851:                }
0852:                for (PrimaryIndex index : priIndexMap.values()) {
0853:                    firstException = closeDb(index.getDatabase(),
0854:                            firstException);
0855:                }
0856:                if (firstException != null) {
0857:                    throw firstException;
0858:                }
0859:            }
0860:
0861:            public synchronized Sequence getSequence(String name)
0862:                    throws DatabaseException {
0863:
0864:                checkOpen();
0865:
0866:                if (storeConfig.getReadOnly()) {
0867:                    throw new IllegalStateException("Store is read-only");
0868:                }
0869:
0870:                Sequence seq = sequenceMap.get(name);
0871:                if (seq == null) {
0872:                    if (sequenceDb == null) {
0873:                        String dbName = storePrefix + SEQUENCE_DB;
0874:                        DatabaseConfig dbConfig = new DatabaseConfig();
0875:                        dbConfig.setTransactional(storeConfig
0876:                                .getTransactional());
0877:                        dbConfig.setAllowCreate(true);
0878:                        sequenceDb = env.openDatabase(null, dbName, dbConfig);
0879:                    }
0880:                    DatabaseEntry entry = new DatabaseEntry();
0881:                    StringBinding.stringToEntry(name, entry);
0882:                    seq = sequenceDb.openSequence(null, entry,
0883:                            getSequenceConfig(name));
0884:                    sequenceMap.put(name, seq);
0885:                }
0886:                return seq;
0887:            }
0888:
0889:            public synchronized SequenceConfig getSequenceConfig(String name) {
0890:                checkOpen();
0891:                SequenceConfig config = sequenceConfigMap.get(name);
0892:                if (config == null) {
0893:                    config = new SequenceConfig();
0894:                    config.setInitialValue(1);
0895:                    config.setRange(1, Long.MAX_VALUE);
0896:                    config.setCacheSize(100);
0897:                    config.setAutoCommitNoSync(true);
0898:                    config.setAllowCreate(!storeConfig.getReadOnly());
0899:                    sequenceConfigMap.put(name, config);
0900:                }
0901:                return config;
0902:            }
0903:
0904:            public synchronized void setSequenceConfig(String name,
0905:                    SequenceConfig config) {
0906:                checkOpen();
0907:                sequenceConfigMap.put(name, config);
0908:            }
0909:
0910:            public synchronized DatabaseConfig getPrimaryConfig(
0911:                    Class entityClass) {
0912:                checkOpen();
0913:                String clsName = entityClass.getName();
0914:                EntityMetadata meta = checkEntityClass(clsName);
0915:                return getPrimaryConfig(meta).cloneConfig();
0916:            }
0917:
0918:            private synchronized DatabaseConfig getPrimaryConfig(
0919:                    EntityMetadata meta) {
0920:                String clsName = meta.getClassName();
0921:                DatabaseConfig config = priConfigMap.get(clsName);
0922:                if (config == null) {
0923:                    config = new DatabaseConfig();
0924:                    config.setTransactional(storeConfig.getTransactional());
0925:                    config.setAllowCreate(!storeConfig.getReadOnly());
0926:                    config.setReadOnly(storeConfig.getReadOnly());
0927:                    DbCompat.setDeferredWrite(config, storeConfig
0928:                            .getDeferredWrite());
0929:                    setBtreeComparator(config, meta.getPrimaryKey()
0930:                            .getClassName());
0931:                    priConfigMap.put(clsName, config);
0932:                }
0933:                return config;
0934:            }
0935:
0936:            public synchronized void setPrimaryConfig(Class entityClass,
0937:                    DatabaseConfig config) {
0938:                checkOpen();
0939:                String clsName = entityClass.getName();
0940:                if (priIndexMap.containsKey(clsName)) {
0941:                    throw new IllegalStateException(
0942:                            "Cannot set config after DB is open");
0943:                }
0944:                EntityMetadata meta = checkEntityClass(clsName);
0945:                DatabaseConfig dbConfig = getPrimaryConfig(meta);
0946:                if (config.getSortedDuplicates()
0947:                        || config.getBtreeComparator() != dbConfig
0948:                                .getBtreeComparator()) {
0949:                    throw new IllegalArgumentException(
0950:                            "One of these properties was illegally changed: "
0951:                                    + " SortedDuplicates or BtreeComparator");
0952:                }
0953:                priConfigMap.put(clsName, config);
0954:            }
0955:
0956:            public synchronized SecondaryConfig getSecondaryConfig(
0957:                    Class entityClass, String keyName) {
0958:                checkOpen();
0959:                String entityClsName = entityClass.getName();
0960:                EntityMetadata entityMeta = checkEntityClass(entityClsName);
0961:                SecondaryKeyMetadata secKeyMeta = checkSecKey(entityMeta,
0962:                        keyName);
0963:                String keyClassName = getSecKeyClass(secKeyMeta);
0964:                String secName = makeSecName(entityClass.getName(), keyName);
0965:                return (SecondaryConfig) getSecondaryConfig(secName,
0966:                        entityMeta, keyClassName, secKeyMeta).cloneConfig();
0967:            }
0968:
0969:            private SecondaryConfig getSecondaryConfig(String secName,
0970:                    EntityMetadata entityMeta, String keyClassName,
0971:                    SecondaryKeyMetadata secKeyMeta) {
0972:                SecondaryConfig config = secConfigMap.get(secName);
0973:                if (config == null) {
0974:                    /* Set common properties to match the primary DB. */
0975:                    DatabaseConfig priConfig = getPrimaryConfig(entityMeta);
0976:                    config = new SecondaryConfig();
0977:                    config.setTransactional(priConfig.getTransactional());
0978:                    config.setAllowCreate(!priConfig.getReadOnly());
0979:                    config.setReadOnly(priConfig.getReadOnly());
0980:                    DbCompat.setDeferredWrite(config, DbCompat
0981:                            .getDeferredWrite(priConfig));
0982:                    /* Set secondary properties based on metadata. */
0983:                    config.setAllowPopulate(true);
0984:                    Relationship rel = secKeyMeta.getRelationship();
0985:                    config.setSortedDuplicates(rel == Relationship.MANY_TO_ONE
0986:                            || rel == Relationship.MANY_TO_MANY);
0987:                    setBtreeComparator(config, secKeyMeta.getClassName());
0988:                    PersistKeyCreator keyCreator = new PersistKeyCreator(
0989:                            catalog, entityMeta, keyClassName, secKeyMeta);
0990:                    if (rel == Relationship.ONE_TO_MANY
0991:                            || rel == Relationship.MANY_TO_MANY) {
0992:                        config.setMultiKeyCreator(keyCreator);
0993:                    } else {
0994:                        config.setKeyCreator(keyCreator);
0995:                    }
0996:                    DeleteAction deleteAction = secKeyMeta.getDeleteAction();
0997:                    if (deleteAction != null) {
0998:                        ForeignKeyDeleteAction baseDeleteAction;
0999:                        switch (deleteAction) {
1000:                        case ABORT:
1001:                            baseDeleteAction = ForeignKeyDeleteAction.ABORT;
1002:                            break;
1003:                        case CASCADE:
1004:                            baseDeleteAction = ForeignKeyDeleteAction.CASCADE;
1005:                            break;
1006:                        case NULLIFY:
1007:                            baseDeleteAction = ForeignKeyDeleteAction.NULLIFY;
1008:                            break;
1009:                        default:
1010:                            throw new IllegalStateException(deleteAction
1011:                                    .toString());
1012:                        }
1013:                        config.setForeignKeyDeleteAction(baseDeleteAction);
1014:                        if (deleteAction == DeleteAction.NULLIFY) {
1015:                            config.setForeignMultiKeyNullifier(keyCreator);
1016:                        }
1017:                    }
1018:                    secConfigMap.put(secName, config);
1019:                }
1020:                return config;
1021:            }
1022:
1023:            public synchronized void setSecondaryConfig(Class entityClass,
1024:                    String keyName, SecondaryConfig config) {
1025:                checkOpen();
1026:                String entityClsName = entityClass.getName();
1027:                EntityMetadata entityMeta = checkEntityClass(entityClsName);
1028:                SecondaryKeyMetadata secKeyMeta = checkSecKey(entityMeta,
1029:                        keyName);
1030:                String keyClassName = getSecKeyClass(secKeyMeta);
1031:                String secName = makeSecName(entityClass.getName(), keyName);
1032:                if (secIndexMap.containsKey(secName)) {
1033:                    throw new IllegalStateException(
1034:                            "Cannot set config after DB is open");
1035:                }
1036:                SecondaryConfig dbConfig = getSecondaryConfig(secName,
1037:                        entityMeta, keyClassName, secKeyMeta);
1038:                if (config.getSortedDuplicates() != dbConfig
1039:                        .getSortedDuplicates()
1040:                        || config.getBtreeComparator() != dbConfig
1041:                                .getBtreeComparator()
1042:                        || config.getDuplicateComparator() != null
1043:                        || config.getAllowPopulate() != dbConfig
1044:                                .getAllowPopulate()
1045:                        || config.getKeyCreator() != dbConfig.getKeyCreator()
1046:                        || config.getMultiKeyCreator() != dbConfig
1047:                                .getMultiKeyCreator()
1048:                        || config.getForeignKeyNullifier() != dbConfig
1049:                                .getForeignKeyNullifier()
1050:                        || config.getForeignMultiKeyNullifier() != dbConfig
1051:                                .getForeignMultiKeyNullifier()
1052:                        || config.getForeignKeyDeleteAction() != dbConfig
1053:                                .getForeignKeyDeleteAction()
1054:                        || config.getForeignKeyDatabase() != null) {
1055:                    throw new IllegalArgumentException(
1056:                            "One of these properties was illegally changed: "
1057:                                    + " SortedDuplicates, BtreeComparator, DuplicateComparator,"
1058:                                    + " AllowPopulate, KeyCreator, MultiKeyCreator,"
1059:                                    + " ForeignKeyNullifer, ForeignMultiKeyNullifier,"
1060:                                    + " ForeignKeyDeleteAction, ForeignKeyDatabase");
1061:                }
1062:                secConfigMap.put(secName, config);
1063:            }
1064:
1065:            private static String makeSecName(String entityClsName,
1066:                    String keyName) {
1067:                return entityClsName + NAME_SEPARATOR + keyName;
1068:            }
1069:
1070:            static String makePriDbName(String storePrefix, String entityClsName) {
1071:                return storePrefix + entityClsName;
1072:            }
1073:
1074:            static String makeSecDbName(String storePrefix,
1075:                    String entityClsName, String keyName) {
1076:                return storePrefix + makeSecName(entityClsName, keyName);
1077:            }
1078:
1079:            private void checkOpen() {
1080:                if (catalog == null) {
1081:                    throw new IllegalStateException("Store has been closed");
1082:                }
1083:            }
1084:
1085:            private EntityMetadata checkEntityClass(String clsName) {
1086:                EntityMetadata meta = model.getEntityMetadata(clsName);
1087:                if (meta == null) {
1088:                    throw new IllegalArgumentException(
1089:                            "Class could not be loaded or is not an entity class: "
1090:                                    + clsName);
1091:                }
1092:                return meta;
1093:            }
1094:
1095:            private SecondaryKeyMetadata checkSecKey(EntityMetadata entityMeta,
1096:                    String keyName) {
1097:                SecondaryKeyMetadata secKeyMeta = entityMeta.getSecondaryKeys()
1098:                        .get(keyName);
1099:                if (secKeyMeta == null) {
1100:                    throw new IllegalArgumentException("Not a secondary key: "
1101:                            + makeSecName(entityMeta.getClassName(), keyName));
1102:                }
1103:                return secKeyMeta;
1104:            }
1105:
1106:            private String getSecKeyClass(SecondaryKeyMetadata secKeyMeta) {
1107:                String clsName = secKeyMeta.getElementClassName();
1108:                if (clsName == null) {
1109:                    clsName = secKeyMeta.getClassName();
1110:                }
1111:                return SimpleCatalog.keyClassName(clsName);
1112:            }
1113:
1114:            private PersistKeyBinding getKeyBinding(String keyClassName) {
1115:                PersistKeyBinding binding = keyBindingMap.get(keyClassName);
1116:                if (binding == null) {
1117:                    binding = new PersistKeyBinding(catalog, keyClassName,
1118:                            rawAccess);
1119:                    keyBindingMap.put(keyClassName, binding);
1120:                }
1121:                return binding;
1122:            }
1123:
1124:            private void setBtreeComparator(DatabaseConfig config,
1125:                    String clsName) {
1126:                if (!rawAccess) {
1127:                    ClassMetadata meta = model.getClassMetadata(clsName);
1128:                    if (meta != null) {
1129:                        List<FieldMetadata> compositeKeyFields = meta
1130:                                .getCompositeKeyFields();
1131:                        if (compositeKeyFields != null) {
1132:                            Class keyClass = SimpleCatalog
1133:                                    .keyClassForName(clsName);
1134:                            if (Comparable.class.isAssignableFrom(keyClass)) {
1135:                                Comparator<Object> cmp = new PersistComparator(
1136:                                        clsName, compositeKeyFields,
1137:                                        getKeyBinding(clsName));
1138:                                config.setBtreeComparator(cmp);
1139:                            }
1140:                        }
1141:                    }
1142:                }
1143:            }
1144:
1145:            private DatabaseException closeDb(Database db,
1146:                    DatabaseException firstException) {
1147:                if (db != null) {
1148:                    try {
1149:                        db.close();
1150:                    } catch (DatabaseException e) {
1151:                        if (firstException == null) {
1152:                            firstException = e;
1153:                        }
1154:                    }
1155:                }
1156:                return firstException;
1157:            }
1158:
1159:            public EvolveStats evolve(EvolveConfig config)
1160:                    throws DatabaseException {
1161:
1162:                checkOpen();
1163:                List<Format> toEvolve = new ArrayList<Format>();
1164:                Set<String> configToEvolve = config.getClassesToEvolve();
1165:                if (configToEvolve.isEmpty()) {
1166:                    catalog.getEntityFormats(toEvolve);
1167:                } else {
1168:                    for (String name : configToEvolve) {
1169:                        Format format = catalog.getFormat(name);
1170:                        if (format == null) {
1171:                            throw new IllegalArgumentException(
1172:                                    "Class to evolve is not persistent: "
1173:                                            + name);
1174:                        }
1175:                        if (!format.isEntity()) {
1176:                            throw new IllegalArgumentException(
1177:                                    "Class to evolve is not an entity class: "
1178:                                            + name);
1179:                        }
1180:                        toEvolve.add(format);
1181:                    }
1182:                }
1183:
1184:                EvolveEvent event = EvolveInternal.newEvent();
1185:                for (Format format : toEvolve) {
1186:                    if (format.getEvolveNeeded()) {
1187:                        evolveIndex(format, event, config.getEvolveListener());
1188:                        format.setEvolveNeeded(false);
1189:                        catalog.flush();
1190:                    }
1191:                }
1192:
1193:                return event.getStats();
1194:            }
1195:
1196:            private void evolveIndex(Format format, EvolveEvent event,
1197:                    EvolveListener listener) throws DatabaseException {
1198:
1199:                Class entityClass = format.getType();
1200:                String entityClassName = format.getClassName();
1201:                EntityMetadata meta = model.getEntityMetadata(entityClassName);
1202:                String keyClassName = meta.getPrimaryKey().getClassName();
1203:                keyClassName = SimpleCatalog.keyClassName(keyClassName);
1204:                DatabaseConfig dbConfig = getPrimaryConfig(meta);
1205:
1206:                PrimaryIndex<Object, Object> index = getPrimaryIndex(
1207:                        Object.class, keyClassName, entityClass,
1208:                        entityClassName);
1209:                Database db = index.getDatabase();
1210:
1211:                EntityBinding binding = index.getEntityBinding();
1212:                DatabaseEntry key = new DatabaseEntry();
1213:                DatabaseEntry data = new DatabaseEntry();
1214:
1215:                Cursor readCursor = db.openCursor(null,
1216:                        CursorConfig.READ_UNCOMMITTED);
1217:                try {
1218:                    while (readCursor.getNext(key, data, null) == OperationStatus.SUCCESS) {
1219:                        if (evolveNeeded(key, data, binding)) {
1220:                            Transaction txn = null;
1221:                            if (dbConfig.getTransactional()) {
1222:                                boolean success = false;
1223:                                txn = env.beginTransaction(null, null);
1224:                            }
1225:                            boolean written = false;
1226:                            Cursor writeCursor = null;
1227:                            try {
1228:                                writeCursor = db.openCursor(txn, null);
1229:                                if (writeCursor.getSearchKey(key, data,
1230:                                        LockMode.RMW) == OperationStatus.SUCCESS) {
1231:                                    if (evolveNeeded(key, data, binding)) {
1232:                                        writeCursor.putCurrent(data);
1233:                                        written = true;
1234:                                    }
1235:                                    if (listener != null) {
1236:                                        EvolveInternal.updateEvent(event,
1237:                                                entityClassName, 1, written ? 1
1238:                                                        : 0);
1239:                                        if (!listener.evolveProgress(event)) {
1240:                                            break;
1241:                                        }
1242:                                    }
1243:                                }
1244:                            } finally {
1245:                                if (writeCursor != null) {
1246:                                    writeCursor.close();
1247:                                }
1248:                                if (txn != null) {
1249:                                    if (written) {
1250:                                        txn.commit();
1251:                                    } else {
1252:                                        txn.abort();
1253:                                    }
1254:                                }
1255:                            }
1256:                        }
1257:                    }
1258:                } finally {
1259:                    readCursor.close();
1260:                }
1261:            }
1262:
1263:            /**
1264:             * Checks whether the given data is in the current format by translating it
1265:             * to/from an object.  If true is returned, data is updated.
1266:             */
1267:            private boolean evolveNeeded(DatabaseEntry key, DatabaseEntry data,
1268:                    EntityBinding binding) {
1269:                Object entity = binding.entryToObject(key, data);
1270:                DatabaseEntry newData = new DatabaseEntry();
1271:                binding.objectToData(entity, newData);
1272:                if (data.equals(newData)) {
1273:                    return false;
1274:                } else {
1275:                    byte[] bytes = newData.getData();
1276:                    int off = newData.getOffset();
1277:                    int size = newData.getSize();
1278:                    data.setData(bytes, off, size);
1279:                    return true;
1280:                }
1281:            }
1282:
1283:            /**
1284:             * For unit testing.
1285:             */
1286:            public static void setSyncHook(SyncHook hook) {
1287:                syncHook = hook;
1288:            }
1289:
1290:            /**
1291:             * For unit testing.
1292:             */
1293:            public interface SyncHook {
1294:                void onSync(Database db, boolean flushLog);
1295:            }
1296:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.