Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2011-09-10 00:47:16 -0400
committerEike Stepper2011-09-10 00:47:16 -0400
commitaece2caa93dd257b4aa99c93d232c73cb96d1f9e (patch)
tree18442c3f03db7a665ff436d927919506327dc3e8
downloadcdo-bugs/351078.tar.gz
cdo-bugs/351078.tar.xz
cdo-bugs/351078.zip
-rw-r--r--org.eclipse.emf.cdo.server.db/.classpath7
-rw-r--r--org.eclipse.emf.cdo.server.db/.options3
-rw-r--r--org.eclipse.emf.cdo.server.db/.project44
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/.api_filters12
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs373
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs3
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs120
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs3
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs94
-rw-r--r--org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs31
-rw-r--r--org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF22
-rw-r--r--org.eclipse.emf.cdo.server.db/about.html28
-rw-r--r--org.eclipse.emf.cdo.server.db/about.ini15
-rw-r--r--org.eclipse.emf.cdo.server.db/about.mappings6
-rw-r--r--org.eclipse.emf.cdo.server.db/about.properties31
-rw-r--r--org.eclipse.emf.cdo.server.db/build.properties29
-rw-r--r--org.eclipse.emf.cdo.server.db/copyright.txt8
-rw-r--r--org.eclipse.emf.cdo.server.db/modeling32.pngbin0 -> 2414 bytes
-rw-r--r--org.eclipse.emf.cdo.server.db/plugin.properties13
-rw-r--r--org.eclipse.emf.cdo.server.db/plugin.xml57
-rw-r--r--org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd113
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java202
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java70
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java32
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java26
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java89
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java110
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java52
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java287
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java43
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java201
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java44
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java43
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java113
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java47
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java348
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java278
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java15
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java15
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java47
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java267
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java67
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java208
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java40
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java727
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java1393
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java94
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java130
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java687
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java302
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java53
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java265
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java429
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java69
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java138
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java364
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java291
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java249
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java237
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java45
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java81
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java641
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java882
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java65
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java447
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java113
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java588
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java870
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java480
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java485
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java69
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java68
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java1212
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java67
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java1084
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java52
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java72
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java1502
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java71
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java1411
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/DelegatingObjectTypeMapper.java127
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java742
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategy.java72
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditMappingStrategyWithRanges.java76
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java1124
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategy.java196
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingMappingStrategyWithRanges.java95
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalMappingStrategy.java272
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java764
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditMappingStrategy.java62
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java590
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java825
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeCache.java98
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java256
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/Messages.java41
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/messages/messages.properties31
-rw-r--r--org.eclipse.emf.cdo.server/.classpath7
-rw-r--r--org.eclipse.emf.cdo.server/.options11
-rw-r--r--org.eclipse.emf.cdo.server/.project44
-rw-r--r--org.eclipse.emf.cdo.server/.settings/.api_filters324
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.core.prefs373
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.launching.prefs3
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.jdt.ui.prefs120
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.ltk.core.refactoring.prefs3
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.tasks.ui.prefs4
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.mylyn.team.ui.prefs3
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.api.tools.prefs94
-rw-r--r--org.eclipse.emf.cdo.server/.settings/org.eclipse.pde.prefs31
-rw-r--r--org.eclipse.emf.cdo.server/CDOServer.launch368
-rw-r--r--org.eclipse.emf.cdo.server/CDOServer_SSL.launch368
-rw-r--r--org.eclipse.emf.cdo.server/META-INF/MANIFEST.MF29
-rw-r--r--org.eclipse.emf.cdo.server/about.html28
-rw-r--r--org.eclipse.emf.cdo.server/about.ini15
-rw-r--r--org.eclipse.emf.cdo.server/about.mappings6
-rw-r--r--org.eclipse.emf.cdo.server/about.properties31
-rw-r--r--org.eclipse.emf.cdo.server/build.properties32
-rw-r--r--org.eclipse.emf.cdo.server/copyright.txt8
-rw-r--r--org.eclipse.emf.cdo.server/modeling32.pngbin0 -> 2414 bytes
-rw-r--r--org.eclipse.emf.cdo.server/plugin.properties17
-rw-r--r--org.eclipse.emf.cdo.server/plugin.xml54
-rw-r--r--org.eclipse.emf.cdo.server/schema/appExtensions.exsd103
-rw-r--r--org.eclipse.emf.cdo.server/schema/repositoryFactories.exsd113
-rw-r--r--org.eclipse.emf.cdo.server/schema/storeFactories.exsd113
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/CommitManager.java191
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java109
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingRepository.java286
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java694
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryManager.java319
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/QueryResult.java35
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java1923
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ResourcesQueryHandler.java146
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java901
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java574
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java482
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java230
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java88
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java1404
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java287
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XATransactionCommitContext.java185
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/XRefsQueryHandler.java357
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOCommandProvider.java405
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java137
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/OM.java66
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSession.java98
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionConfiguration.java153
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java584
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java109
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java1310
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java502
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java67
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreFactory.java39
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/Messages.java41
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/messages/messages.properties16
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/FailoverParticipant.java131
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/OfflineClone.java249
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/ReplicatorCommitContext.java162
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/RepositorySynchronizer.java650
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java788
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java1220
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerExporter.java695
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerImporter.java663
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerUtil.java299
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ContainmentCycleDetectedException.java39
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IMEMStore.java26
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryContext.java41
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandler.java25
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IQueryHandlerProvider.java26
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java264
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryFactory.java27
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositoryProvider.java20
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepositorySynchronizer.java52
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISession.java60
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java35
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStore.java213
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java729
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreChunkReader.java119
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreFactory.java29
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISynchronizableRepository.java43
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java24
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java29
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/RepositoryNotFoundException.java37
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/StoreThreadLocal.java118
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSession.java21
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/CDOSessionConfiguration.java33
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/embedded/package-info.java15
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/IMEMStore.java56
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/MEMStoreUtil.java32
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/mem/package-info.java15
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/package-info.java23
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerQueryHandlerProvider.java44
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ContainerRepositoryProvider.java49
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/DurableLockArea.java108
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/FactoriesQueryHandlerProvider.java76
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/IAppExtension.java26
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java63
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java103
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitManager.java47
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalFailoverParticipant.java24
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java139
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryManager.java32
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalQueryResult.java32
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java230
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepositorySynchronizer.java35
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java86
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java79
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalStore.java112
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSynchronizableRepository.java36
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalTransaction.java24
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java56
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStore.java120
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/LongIDStoreAccessor.java50
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ObjectWriteAccessHandler.java159
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/PluginRepositoryProvider.java27
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/QueryHandlerFactory.java35
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java346
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryFactory.java45
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryUserManager.java147
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java549
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java180
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorBase.java508
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessorPool.java121
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreChunkReader.java72
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/SyncingUtil.java60
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/package-info.java18
228 files changed, 49659 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server.db/.classpath b/org.eclipse.emf.cdo.server.db/.classpath
new file mode 100644
index 0000000000..64c5e31b7a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.emf.cdo.server.db/.options b/org.eclipse.emf.cdo.server.db/.options
new file mode 100644
index 0000000000..f4f74f846d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.options
@@ -0,0 +1,3 @@
+# Debugging and tracing options
+
+org.eclipse.emf.cdo.server.db/debug = true
diff --git a/org.eclipse.emf.cdo.server.db/.project b/org.eclipse.emf.cdo.server.db/.project
new file mode 100644
index 0000000000..4bb79d4880
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.project
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.emf.cdo.server.db</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.emf.cdo.releng.version.VersionBuilder</name>
+ <arguments>
+ <dictionary>
+ <key>release.path</key>
+ <value>/org.eclipse.emf.cdo.releng/release.xml</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+ <nature>org.eclipse.emf.cdo.releng.version.VersionNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.emf.cdo.server.db/.settings/.api_filters b/org.eclipse.emf.cdo.server.db/.settings/.api_filters
new file mode 100644
index 0000000000..475cf591c8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/.api_filters
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.emf.cdo.server.db" version="2">
+ <resource path="src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java" type="org.eclipse.emf.cdo.server.internal.db.DBStoreAccessor">
+ <filter id="574660632">
+ <message_arguments>
+ <message_argument value="DurableLocking"/>
+ <message_argument value="IDurableLockingManager"/>
+ <message_argument value="DBStoreAccessor"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..eca460f479
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Jul 04 13:01:28 CEST 2011
+eclipse.preferences.version=1
+encoding//model/org.eclipse.emf.cdo.defs.ecorediag=UTF-8
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..611d1a92fb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,373 @@
+#Fri Sep 02 05:40:11 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,*.ucls,doc-files/,package.html,package-info.java
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
+org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,HIGH,LOW,LOW,LOW,LOW,LOW
+org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,PERF,MEM,POLISH,@generated NOT,@ADDED
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs
new file mode 100644
index 0000000000..4658ec1435
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.launching.prefs
@@ -0,0 +1,3 @@
+#Fri Sep 02 05:38:34 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.launching.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE=ignore
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..4277817dad
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,120 @@
+#Thu Feb 04 09:44:24 CET 2010
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=true
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=true
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=false
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=true
+cleanup.use_this_for_non_static_field_access=true
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=true
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_EMFT
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_EMFT
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=ex
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=org.eclipse.emf.cdo;org.eclipse.emf.internal.cdo;org.eclipse.net4j;org.eclipse.internal.net4j;org.eclipse.emf;org.eclipse;com;org;javax;java;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="false" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment"/><template autoinsert\="false" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment"/><template autoinsert\="false" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment"/><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n * \r\n * Contributors\:\r\n * Eike Stepper - initial API and implementation\r\n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author Eike Stepper\r\n */</template><template autoinsert\="false" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment"/><template autoinsert\="false" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment"/><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="false" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment"/><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="false" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">${exception_var}.printStackTrace();</template><template autoinsert\="false" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">${body_statement}</template><template autoinsert\="false" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=true
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=true
+sp_cleanup.use_this_for_non_static_field_access=true
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=true
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs
new file mode 100644
index 0000000000..864e30fe5d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.ltk.core.refactoring.prefs
@@ -0,0 +1,3 @@
+#Thu Feb 04 09:44:24 CET 2010
+eclipse.preferences.version=1
+org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000000..b050639a54
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Thu Feb 04 09:44:24 CET 2010
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000000..2f50f36c0c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Thu Feb 04 09:44:24 CET 2010
+commit.comment.template=[${task.id}] ${task.description} \r\n${task.url}
+eclipse.preferences.version=1
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000000..f8d0a0660e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,94 @@
+#Thu Feb 04 09:44:24 CET 2010
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Ignore
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Ignore
+automatically_removed_unused_problem_filters=Disabled
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000000..c6b96bb45e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,31 @@
+#Thu Feb 04 09:44:24 CET 2010
+compilers.f.unresolved-features=1
+compilers.f.unresolved-plugins=1
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.build.bin.includes=1
+compilers.p.build.java.compliance=1
+compilers.p.build.missing.output=2
+compilers.p.build.output.library=1
+compilers.p.build.source.library=1
+compilers.p.build.src.includes=1
+compilers.p.deprecated=1
+compilers.p.discouraged-class=1
+compilers.p.internal=1
+compilers.p.missing-packages=1
+compilers.p.missing-version-export-package=1
+compilers.p.missing-version-import-package=1
+compilers.p.missing-version-require-bundle=1
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=2
+compilers.p.unknown-attribute=1
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-identifier=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.s.create-docs=false
+compilers.s.doc-folder=doc
+compilers.s.open-tags=1
+eclipse.preferences.version=1
diff --git a/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF b/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..b3af04016e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.emf.cdo.server.db;singleton:=true
+Bundle-Version: 4.1.0.qualifier
+Bundle-Name: %pluginName
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.eclipse.emf.cdo.server.internal.db.bundle.OM$Activator
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ClassPath: .
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.net4j.db;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
+ org.eclipse.emf.cdo.server;bundle-version="[4.0.0,5.0.0)";visibility:=reexport
+Export-Package: org.eclipse.emf.cdo.server.db;version="4.1.0",
+ org.eclipse.emf.cdo.server.db.mapping;version="4.1.0",
+ org.eclipse.emf.cdo.server.internal.db;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db",
+ org.eclipse.emf.cdo.server.internal.db.bundle;version="4.1.0";x-internal:=true,
+ org.eclipse.emf.cdo.server.internal.db.jdbc;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db",
+ org.eclipse.emf.cdo.server.internal.db.mapping;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db",
+ org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;version="4.1.0";x-friends:="org.eclipse.emf.cdo.tests,org.eclipse.emf.cdo.tests.db",
+ org.eclipse.emf.cdo.server.internal.db.messages;version="4.1.0";x-internal:=true
diff --git a/org.eclipse.emf.cdo.server.db/about.html b/org.eclipse.emf.cdo.server.db/about.html
new file mode 100644
index 0000000000..d35d5aed64
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2007</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/org.eclipse.emf.cdo.server.db/about.ini b/org.eclipse.emf.cdo.server.db/about.ini
new file mode 100644
index 0000000000..32006ae5d6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.ini
@@ -0,0 +1,15 @@
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%featureText
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=modeling32.png
+
+# Property "appName" contains name of the application (translated)
+appName=%featureName
+
diff --git a/org.eclipse.emf.cdo.server.db/about.mappings b/org.eclipse.emf.cdo.server.db/about.mappings
new file mode 100644
index 0000000000..bddaab4310
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.mappings
@@ -0,0 +1,6 @@
+# about.mappings
+# contains fill-ins for about.properties
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file does not need to be translated.
+
+0=@build@ \ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/about.properties b/org.eclipse.emf.cdo.server.db/about.properties
new file mode 100644
index 0000000000..949ccadf4e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.properties
@@ -0,0 +1,31 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+# NLS_MESSAGEFORMAT_VAR
+
+# ==============================================================================
+# Do not change the properties between this line and the last line containing:
+# %%% END OF TRANSLATED PROPERTIES %%%
+# Instead, either redefine an existing property, or create a new property,
+# append it to the end of the file, and change the code to use the new name.
+# ==============================================================================
+
+featureName = CDO Model Repository Server DB
+featureText = CDO Model Repository Server DB\n\
+Version: {featureVersion}\n\
+Build id: {0}\n\
+\n\
+Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. All rights reserved.\n\
+\n\
+Visit http://www.eclipse.org/cdo
+
+# ==============================================================================
+# %%% END OF TRANSLATED PROPERTIES %%%
+# The above properties have been shipped for translation.
+# ==============================================================================
diff --git a/org.eclipse.emf.cdo.server.db/build.properties b/org.eclipse.emf.cdo.server.db/build.properties
new file mode 100644
index 0000000000..7962001d93
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/build.properties
@@ -0,0 +1,29 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+# NLS_MESSAGEFORMAT_VAR
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ .options,\
+ about.html,\
+ copyright.txt,\
+ plugin.xml,\
+ schema/,\
+ plugin.properties,\
+ about.ini,\
+ about.mappings,\
+ about.properties,\
+ modeling32.png
+src.includes = about.html,\
+ copyright.txt
+
+org.eclipse.emf.cdo.releng.javadoc.project = org.eclipse.emf.cdo.doc
diff --git a/org.eclipse.emf.cdo.server.db/copyright.txt b/org.eclipse.emf.cdo.server.db/copyright.txt
new file mode 100644
index 0000000000..e921242cf0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/copyright.txt
@@ -0,0 +1,8 @@
+Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+ Eike Stepper - initial API and implementation \ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/modeling32.png b/org.eclipse.emf.cdo.server.db/modeling32.png
new file mode 100644
index 0000000000..6b08de2ada
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/modeling32.png
Binary files differ
diff --git a/org.eclipse.emf.cdo.server.db/plugin.properties b/org.eclipse.emf.cdo.server.db/plugin.properties
new file mode 100644
index 0000000000..ade0c88bdc
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/plugin.properties
@@ -0,0 +1,13 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+pluginName = CDO Model Repository Server DB
+providerName = Eclipse Modeling Project
+
+extension-point.name = CDO Mapping Strategies \ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/plugin.xml b/org.eclipse.emf.cdo.server.db/plugin.xml
new file mode 100644
index 0000000000..6abb1a84a7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/plugin.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Public License v1.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/legal/epl-v10.html
+
+ Contributors:
+ Eike Stepper - initial API and implementation
+-->
+
+<plugin>
+
+ <extension-point id="mappingStrategies" name="%extension-point.name" schema="schema/mappingStrategies.exsd"/>
+
+ <extension
+ point="org.eclipse.net4j.util.factories">
+ <factory
+ class="org.eclipse.emf.cdo.server.internal.db.DBBrowserPage$Factory"
+ productGroup="org.eclipse.emf.cdo.server.browserPages"
+ type="db">
+ </factory>
+ </extension>
+
+ <extension
+ point="org.eclipse.emf.cdo.server.storeFactories">
+ <storeFactory
+ class="org.eclipse.emf.cdo.server.internal.db.DBStoreFactory"
+ storeType="db">
+ </storeFactory>
+ </extension>
+
+ <extension
+ point="org.eclipse.emf.cdo.server.db.mappingStrategies">
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalMappingStrategy"
+ type="horizontal"/>
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalNonAuditMappingStrategy"
+ type="horizontalNonAuditing"/>
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategy"
+ type="horizontalAuditing"/>
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategy"
+ type="horizontalBranching"/>
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategyWithRanges"
+ type="horizontalAuditingWithRanges"/>
+ <mappingStrategy
+ class="org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategyWithRanges"
+ type="horizontalBranchingWithRanges"/>
+ </extension>
+
+</plugin>
diff --git a/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd
new file mode 100644
index 0000000000..c708d5537b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd
@@ -0,0 +1,113 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.cdo.server.db">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.emf.cdo.server.db" id="mappingStrategies" name="CDO Mapping Strategies"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="mappingStrategy" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="mappingStrategy">
+ <complexType>
+ <attribute name="type" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.emf.cdo.server.db.IMappingStrategy"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at &lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java
new file mode 100644
index 0000000000..b5cb29a567
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java
@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.DBBrowserPage;
+import org.eclipse.emf.cdo.server.internal.db.DBStore;
+import org.eclipse.emf.cdo.server.internal.db.SmartPreparedStatementCache;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategyWithRanges;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategyWithRanges;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalNonAuditMappingStrategy;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * @author Eike Stepper
+ */
+public final class CDODBUtil
+{
+ /**
+ * @since 2.0
+ */
+ public static final int DEFAULT_STATEMENT_CACHE_CAPACITY = 200;
+
+ /**
+ * @since 2.0
+ */
+ public static final String EXT_POINT_MAPPING_STRATEGIES = "mappingStrategies"; //$NON-NLS-1$
+
+ /**
+ * @since 4.1
+ */
+ public static final String PROP_WITH_RANGES = "withRanges";
+
+ /**
+ * @since 4.1
+ */
+ public static final String PROP_COPY_ON_BRANCH = "copyOnBranch";
+
+ private CDODBUtil()
+ {
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static void prepareContainer(IManagedContainer container)
+ {
+ container.registerFactory(new DBBrowserPage.Factory());
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IDBStore createStore(IMappingStrategy mappingStrategy, IDBAdapter dbAdapter,
+ IDBConnectionProvider dbConnectionProvider)
+ {
+ DBStore store = new DBStore();
+ store.setMappingStrategy(mappingStrategy);
+ store.setDBAdapter(dbAdapter);
+ store.setDbConnectionProvider(dbConnectionProvider);
+ return store;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing)
+ {
+ return createHorizontalMappingStrategy(auditing, false, false);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching)
+ {
+ return createHorizontalMappingStrategy(auditing, branching, false);
+ }
+
+ /**
+ * @since 4.1
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching, boolean withRanges)
+ {
+ if (branching)
+ {
+ if (auditing)
+ {
+ if (withRanges)
+ {
+ return new HorizontalBranchingMappingStrategyWithRanges();
+ }
+
+ return new HorizontalBranchingMappingStrategy();
+ }
+
+ throw new IllegalArgumentException("Misconfiguration: Branching requires Auditing!");
+ }
+
+ if (auditing)
+ {
+ if (withRanges)
+ {
+ return new HorizontalAuditMappingStrategyWithRanges();
+ }
+
+ return new HorizontalAuditMappingStrategy();
+ }
+
+ return new HorizontalNonAuditMappingStrategy();
+ }
+
+ /**
+ * Creates a horizontal {@link IMappingStrategy mapping strategy} that supports all valid combinations of auditing and
+ * branching.
+ *
+ * @since 4.1
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy()
+ {
+ return new HorizontalMappingStrategy();
+ }
+
+ /**
+ * Can only be used when Eclipse is running. In standalone scenarios create the mapping strategy instance by directly
+ * calling the constructor of the mapping strategy class.
+ *
+ * @see #createHorizontalMappingStrategy(boolean)
+ * @see #createHorizontalMappingStrategy(boolean, boolean)
+ * @since 2.0
+ */
+ public static IMappingStrategy createMappingStrategy(String type)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, EXT_POINT_MAPPING_STRATEGIES);
+ for (final IConfigurationElement element : elements)
+ {
+ if ("mappingStrategy".equals(element.getName())) //$NON-NLS-1$
+ {
+ String typeAttr = element.getAttribute("type"); //$NON-NLS-1$
+ if (ObjectUtil.equals(typeAttr, type))
+ {
+ try
+ {
+ return (IMappingStrategy)element.createExecutableExtension("class"); //$NON-NLS-1$
+ }
+ catch (CoreException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a prepared statement cache with the {@link CDODBUtil#DEFAULT_STATEMENT_CACHE_CAPACITY default capacity}.
+ *
+ * @since 2.0
+ * @see CDODBUtil#createStatementCache(int)
+ */
+ public static IPreparedStatementCache createStatementCache()
+ {
+ return createStatementCache(DEFAULT_STATEMENT_CACHE_CAPACITY);
+ }
+
+ /**
+ * Creates a prepared statement cache with the given capacity.
+ *
+ * @since 2.0
+ */
+ public static IPreparedStatementCache createStatementCache(int capacity)
+ {
+ return new SmartPreparedStatementCache(capacity);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java
new file mode 100644
index 0000000000..c1c6d23498
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStore extends IStore, IDBConnectionProvider, CanHandleClientAssignedIDs
+{
+ /**
+ * @since 2.0
+ */
+ public IMappingStrategy getMappingStrategy();
+
+ /**
+ * @since 4.0
+ */
+ public IIDHandler getIDHandler();
+
+ public IDBAdapter getDBAdapter();
+
+ public IDBSchema getDBSchema();
+
+ /**
+ * Get the meta data manager associated with this DBStore.
+ *
+ * @since 2.0
+ */
+ public IMetaDataManager getMetaDataManager();
+
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getReader(ISession session);
+
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getWriter(ITransaction transaction);
+
+ /**
+ * @since 4.0
+ */
+ public interface Props
+ {
+ public static final String CONNECTION_KEEPALIVE_PERIOD = "connectionKeepAlivePeriod"; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java
new file mode 100644
index 0000000000..2d507942be
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+
+import java.sql.Connection;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStoreAccessor extends IStoreAccessor.Raw
+{
+ public IDBStore getStore();
+
+ public Connection getConnection();
+
+ /**
+ * @since 2.0
+ */
+ public IPreparedStatementCache getStatementCache();
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java
new file mode 100644
index 0000000000..67f9bea078
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.IStoreChunkReader;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStoreChunkReader extends IStoreChunkReader
+{
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getAccessor();
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java
new file mode 100644
index 0000000000..0bbee5d23a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Comparator;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IIDHandler extends Comparator<CDOID>
+{
+ public IDBStore getStore();
+
+ public DBType getDBType();
+
+ public Set<ObjectType> getObjectIDTypes();
+
+ public ITypeMapping getObjectTypeMapping();
+
+ public CDOID createCDOID(String val);
+
+ public boolean isLocalCDOID(CDOID id);
+
+ public CDOID getLastObjectID();
+
+ public void setLastObjectID(CDOID lastObjectID);
+
+ /**
+ * @since 4.1
+ */
+ public void adjustLastObjectID(CDOID maxID);
+
+ public CDOID getNextLocalObjectID();
+
+ public void setNextLocalObjectID(CDOID nextLocalObjectID);
+
+ public CDOID getNextCDOID(CDORevision revision);
+
+ public void appendCDOID(StringBuilder builder, CDOID id);
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException;
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException;
+
+ public CDOID getMinCDOID();
+
+ public CDOID getMaxCDOID();
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime);
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id);
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, OMMonitor fork)
+ throws IOException;
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java
new file mode 100644
index 0000000000..972d735afd
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EPackage;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.Collection;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IMetaDataManager
+{
+ /**
+ * Returns the meta ID of the given {@link EModelElement}. <code> getMetaID(getMetaInstance(x))</code> yields
+ * <code>x</code>
+ *
+ * @param modelElement
+ * the element
+ * @return the corresponding ID
+ * @since 4.0
+ */
+ public CDOID getMetaID(EModelElement modelElement, long commitTime);
+
+ /**
+ * Returns the {@link EModelElement} referred to by the given ID. <code> getMetaInstance(getMetaID(m))</code> yields
+ * <code>m</code>
+ *
+ * @since 4.0
+ */
+ public EModelElement getMetaInstance(CDOID id);
+
+ /**
+ * Loads a package unit from the database.
+ *
+ * @param connection
+ * the DB connection to read from.
+ * @param packageUnit
+ * the package unit to load.
+ * @return the loaded package unit.
+ * @since 2.0
+ */
+ public EPackage[] loadPackageUnit(Connection connection, InternalCDOPackageUnit packageUnit);
+
+ /**
+ * @since 4.0
+ */
+ public void clearMetaIDMappings();
+
+ /**
+ * Reads information about package units present in the database.
+ *
+ * @param connection
+ * the DB connection to read from.
+ * @return a collection of package unit information records which can be passed to
+ * {@link IMetaDataManager#loadPackageUnit(Connection, InternalCDOPackageUnit)} in order to read the EPackage.
+ * @since 2.0
+ */
+ public Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection);
+
+ /**
+ * Write package units to the database.
+ *
+ * @param connection
+ * the DB connection to write to.
+ * @param packageUnits
+ * the package units to write.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 2.0
+ */
+ public void writePackageUnits(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public Collection<InternalCDOPackageUnit> rawImport(Connection connection, CDODataInput in, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor) throws IOException;
+
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java
new file mode 100644
index 0000000000..4c81edc459
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IPreparedStatementCache
+{
+ public void setConnection(Connection connection);
+
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability);
+
+ public void releasePreparedStatement(PreparedStatement ps);
+
+ /**
+ * An enum for the degree of probability to which a prepared statement is reused later on. This is used for managing
+ * the cache of prepared statements so that statements which are more likely reused are kept in the cache longer. Rule
+ * of thumb:
+ * <ul>
+ * <li>For global statements which are used regularly (such as lookup object in cdo_objects) use
+ * {@link ReuseProbability#MAX MAX}.
+ * <li>For constant object-specific statements which are used regularly use {@link ReuseProbability#HIGH HIGH}.
+ * <li>For object-specific statements which are assembled from constants which are used regularly use
+ * {@link ReuseProbability#MEDIUM MEDIUM}.
+ * <li>For all other dynamic statements, like queries, use {@link ReuseProbability#LOW LOW}
+ * </ul>
+ *
+ * @author Stefan Winkler
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ */
+ public static enum ReuseProbability
+ {
+ MAX, HIGH, MEDIUM, LOW;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java
new file mode 100644
index 0000000000..745e96eab9
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java
@@ -0,0 +1,287 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 285270: [DB] Support XSD based models
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.revision.CDORevisionData;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.MetaDataManager;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common
+ * types. Implementors should provide a constructor which the factory (see below) can use and implement
+ * {@link #getResultSetValue(ResultSet)}. If needed, {@link #doSetValue(PreparedStatement, int, Object)} can also be
+ * overridden as a counterpart to {@link #getResultSetValue(ResultSet)}. Finally, an implementor should also implement a
+ * suitable factory for the {@link TypeMappingRegistry} and register it either manually using
+ * {@link IManagedContainer#registerFactory(org.eclipse.net4j.util.factory.IFactory)} or using the Net4j Extension Point
+ * <code>factories</code>.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+public abstract class AbstractTypeMapping implements ITypeMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractTypeMapping.class);
+
+ private IMappingStrategy mappingStrategy;
+
+ private EStructuralFeature feature;
+
+ private IDBField field;
+
+ private DBType dbType;
+
+ /**
+ * Create a new type mapping
+ */
+ public AbstractTypeMapping()
+ {
+ super();
+ }
+
+ public final void setMappingStrategy(IMappingStrategy mappingStrategy)
+ {
+ this.mappingStrategy = mappingStrategy;
+ }
+
+ public final IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public final void setFeature(EStructuralFeature feature)
+ {
+ this.feature = feature;
+ }
+
+ public final EStructuralFeature getFeature()
+ {
+ return feature;
+ }
+
+ public final void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision revision)
+ throws SQLException
+ {
+ setValue(stmt, index, getRevisionValue(revision));
+ }
+
+ public final void setDefaultValue(PreparedStatement stmt, int index) throws SQLException
+ {
+ setValue(stmt, index, getDefaultValue());
+ }
+
+ public final void setValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ if (value == CDORevisionData.NIL)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: converting Revision.NIL to DB-null", feature.getName()); //$NON-NLS-1$
+ }
+
+ stmt.setNull(index, getSqlType());
+ }
+ else if (value == null)
+ {
+ if (feature.isMany() || getDefaultValue() == null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: writing Revision.null as DB.null", feature.getName()); //$NON-NLS-1$
+ }
+
+ stmt.setNull(index, getSqlType());
+ }
+ else
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: converting Revision.null to default value", feature.getName()); //$NON-NLS-1$
+ }
+
+ setDefaultValue(stmt, index);
+ }
+ }
+ else
+ {
+ doSetValue(stmt, index, value);
+ }
+ }
+
+ public final void createDBField(IDBTable table)
+ {
+ createDBField(table, mappingStrategy.getFieldName(feature));
+ }
+
+ public final void createDBField(IDBTable table, String fieldName)
+ {
+ DBType fieldType = getDBType();
+ int fieldLength = getDBLength(fieldType);
+ field = table.addField(fieldName, fieldType, fieldLength);
+ }
+
+ public final void setDBField(IDBTable table, String fieldName)
+ {
+ field = table.getField(fieldName);
+ }
+
+ public final IDBField getField()
+ {
+ return field;
+ }
+
+ public final void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException
+ {
+ Object value = readValue(resultSet);
+ revision.setValue(getFeature(), value);
+ }
+
+ public final Object readValue(ResultSet resultSet) throws SQLException
+ {
+ Object value = getResultSetValue(resultSet);
+ if (resultSet.wasNull())
+ {
+ if (feature.isMany())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = null;
+ }
+ else
+ {
+ if (getDefaultValue() == null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "TypeMapping for {0}: read db.null - setting Revision.null, because of default", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = null;
+ }
+ else
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: read db.null - setting Revision.NIL", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = CDORevisionData.NIL;
+ }
+ }
+ }
+
+ return value;
+ }
+
+ protected Object getDefaultValue()
+ {
+ return feature.getDefaultValue();
+ }
+
+ protected final Object getRevisionValue(InternalCDORevision revision)
+ {
+ return revision.getValue(getFeature());
+ }
+
+ /**
+ * Implementors could override this method to convert a given value to the database representation and set it to the
+ * prepared statement.
+ *
+ * @param stmt
+ * the {@link PreparedStatement} which is used for DB access
+ * @param index
+ * the parameter index in the statement which should be set
+ * @param value
+ * the value of the feature which should be written into the DB
+ */
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setObject(index, value, getSqlType());
+ }
+
+ /**
+ * Returns the SQL type of this TypeMapping. The default implementation considers the type map held by the
+ * {@link MetaDataManager meta-data manager}. Subclasses may override.
+ *
+ * @return The sql type of this TypeMapping.
+ */
+ protected int getSqlType()
+ {
+ return getDBType().getCode();
+ }
+
+ public final void setDBType(DBType dbType)
+ {
+ this.dbType = dbType;
+ }
+
+ public DBType getDBType()
+ {
+ return dbType;
+ }
+
+ protected int getDBLength(DBType type)
+ {
+ String value = DBAnnotation.COLUMN_LENGTH.getValue(feature);
+ if (value != null)
+ {
+ try
+ {
+ return Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ OM.LOG.error("Illegal columnLength annotation of feature " + feature.getName());
+ }
+ }
+
+ // TODO: implement DBAdapter.getDBLength
+ // mappingStrategy.getStore().getDBAdapter().getDBLength(type);
+ // which should then return the correct default field length for the db type
+ return type == DBType.VARCHAR ? 32672 : IDBField.DEFAULT;
+ }
+
+ /**
+ * Subclasses should implement this method to read the value from the result set. Typical implementations should look
+ * similar to this one: <code>resultSet.getString(getField().getName())</code>
+ *
+ * @param resultSet
+ * the result set to read from
+ * @return the result value read (this has to be compatible with the {@link #feature}.
+ */
+ protected abstract Object getResultSetValue(ResultSet resultSet) throws SQLException;
+
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java
new file mode 100644
index 0000000000..4170dbfae6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Descriptor;
+
+import org.eclipse.net4j.util.factory.Factory;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+/**
+ * Abstract implementation for {@link ITypeMapping.Factory}. Implementors should implement their custom
+ * {@link #create(String)} method and construct the factory using their custom descriptor. Subclasses must have a
+ * default constructor!
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+public abstract class AbstractTypeMappingFactory extends Factory implements
+ org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Factory
+{
+ private ITypeMapping.Descriptor descriptor;
+
+ public AbstractTypeMappingFactory(Descriptor descriptor)
+ {
+ super(PRODUCT_GROUP, descriptor.getFactoryType());
+ this.descriptor = descriptor;
+ }
+
+ public abstract ITypeMapping create(String description) throws ProductCreationException;
+
+ public final Descriptor getDescriptor()
+ {
+ return descriptor;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java
new file mode 100644
index 0000000000..9775d42fd5
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Basic interface for class mappings.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMapping
+{
+ /**
+ * @since 3.0
+ */
+ public EClass getEClass();
+
+ /**
+ * Returns all DB tables which are used by this class and all its contained features.
+ *
+ * @return a collection of all tables of this class and all its contained features.
+ * @since 3.0
+ */
+ public List<IDBTable> getDBTables();
+
+ /**
+ * Get the mapping of the many-valued feature.
+ *
+ * @param feature
+ * the feature for which the mapping should be returned. <code>feature.isMany()</code> has to be
+ * <code>true</code>.
+ * @return the list mapping corresponding to the feature.
+ */
+ public IListMapping getListMapping(EStructuralFeature feature);
+
+ /**
+ * @since 3.0
+ */
+ public List<IListMapping> getListMappings();
+
+ /**
+ * @since 4.0
+ */
+ public List<ITypeMapping> getValueMappings();
+
+ /**
+ * Read a revision. The branch and timestamp to be read are derived from the branchPoint which is set to the Revision.
+ * Note that non-audit stores only support {@link CDOBranchPoint#UNSPECIFIED_DATE} and non-branching stores only
+ * support the main branch.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision object into which the data should be read. The revision has to be have its ID set to the
+ * requested object's ID. The version is ignored, as the version parameter is used to determine the version
+ * to be read.
+ * @param listChunk
+ * the chunk size to read attribute lists.
+ * @return <code>true</code>, if the revision has been found and read correctly. <code>false</code> if the revision
+ * could not be found. In this case, the content of <code>revision</code> is undefined.
+ */
+ public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk);
+
+ /**
+ * Write the revision data to the database.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision to write.
+ * @param mapType
+ * <code>true</code> if the type of the object is supposed to be mapped, <code>false</code> otherwise.
+ * @param revise
+ * <code>true</code> if the previous revision is supposed to be revised, <code>false</code> otherwise.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 4.0
+ */
+ public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise,
+ OMMonitor monitor);
+
+ /**
+ * Detaches (deletes) a CDO object leaving a "ghost" revision behind.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param id
+ * the id to revise.
+ * @param version
+ * the last valid version.
+ * @param timeStamp
+ * the timestamp of detach.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 3.0
+ */
+ public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor monitor);
+
+ /**
+ * Create a prepared statement which returns all IDs of instances of the corresponding class.
+ *
+ * @param accessor
+ * the accessor to use to create the statement
+ * @return the prepared statement ready to be executed using <code>result.executeQuery()</code>.
+ * @since 3.0
+ */
+ public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor);
+
+ /**
+ * Create a prepared statement which returns all IDs of instances of the corresponding class.
+ *
+ * @param accessor
+ * the accessor to use to create the statement
+ * @param folderId
+ * the ID of the containing folder. <code>0</code> means none.
+ * @param name
+ * the name of the resource node to look up
+ * @param exactMatch
+ * if <code>true</code>, <code>name</code> must match exactly, otherwise all resource nodes starting with
+ * <code>name</code> are returned.
+ * @param branchPoint
+ * a branchPoint (branch and timestamp). A timestamp in the past if past versions should be looked up. In
+ * case of no audit support, this must be {@link CDORevision#UNSPECIFIED_DATE}. In case of non branching
+ * support the branch id must be equal to {@link CDOBranch#MAIN_BRANCH_ID}.
+ * @return the prepared statement ready to be executed using <code>result.executeQuery()</code>.
+ * @throws ImplementationError
+ * if called on a mapping which does not map an <code>EClass instanceof CDOResourceNode</code>.
+ * @since 3.0
+ */
+ public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
+ boolean exactMatch, CDOBranchPoint branchPoint);
+
+ /**
+ * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following
+ * conditions are met:
+ * <ul>
+ * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>.
+ * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to
+ * <code>revision.getTimeStamp()</code>.
+ * </ul>
+ *
+ * @see IMappingStrategy#handleRevisions(IDBStoreAccessor, org.eclipse.emf.ecore.EClass, CDOBranch, long, boolean,
+ * CDORevisionHandler)
+ * @since 4.0
+ */
+ public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler);
+
+ /**
+ * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges.
+ * DetachedCDORevisions must also be considered!
+ *
+ * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...)
+ * @since 3.0
+ */
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments);
+
+ /**
+ * Retrieve cross-references from DB
+ *
+ * @param idString
+ * a string of the form "(id1, id2, id3, ...)" which can be used directly in SQL to form the where-part
+ * "SELECT * FROM foobar WHERE foobar.target IN [idString]".
+ * @see IStoreAccessor#queryXRefs(QueryXRefsContext)
+ * @since 4.0
+ */
+ public boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java
new file mode 100644
index 0000000000..5a1b13f4b4
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+/**
+ * Interface which complements {@link IClassMapping} with methods to facilitate audit support.
+ *
+ * @see IMappingStrategy#hasAuditSupport()
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMappingAuditSupport
+{
+ /**
+ * Read a specific version of a revision. If this method returns <code>true</code> it is guaranteed that
+ * <code>revision.getVersion() == version</code>
+ *
+ * @param storeAccessor
+ * the accessor to use.
+ * @param revision
+ * the revision object into which the data should be read. The revision has to be have its ID set to the
+ * requested object's ID. The version is ignored, as the version parameter is used to determine the version
+ * to be read.
+ * @param listChunk
+ * the chunk size to read attribute lists.
+ * @return <code>true</code>, if the revision has been found and read correctly. <code>false</code> if the revision
+ * could not be found. In this case, the content of <code>revision</code> is undefined.
+ * @since 3.0
+ */
+ public boolean readRevisionByVersion(IDBStoreAccessor storeAccessor, InternalCDORevision revision, int listChunk);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java
new file mode 100644
index 0000000000..86bf12ed72
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+/**
+ * Interface which complements {@link IClassMapping} with methods to facilitate revision delta support.
+ *
+ * @see IMappingStrategy#hasDeltaSupport()
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMappingDeltaSupport
+{
+ /**
+ * Write a revision delta.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param delta
+ * the delta to write.
+ * @param created
+ * the creation timestamp of the new version
+ * @param monitor
+ * the monitor to report progress.
+ */
+ public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
+ OMMonitor monitor);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java
new file mode 100644
index 0000000000..bb09fa2e0d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.ddl.IDBTable;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Interface for mapping features with <code>isMany() == true</code>.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IListMapping
+{
+ /**
+ * Return the mapped feature.
+ *
+ * @return the mapped feature.
+ */
+ public EStructuralFeature getFeature();
+
+ /**
+ * Returns all DB tables which are used by this feature.
+ *
+ * @return a collection of all tables of this feature.
+ */
+ public Collection<IDBTable> getDBTables();
+
+ /**
+ * Write a complete list of values to the database.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision containing the list to be written.
+ */
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision);
+
+ /**
+ * Read the list size and the complete list or the first part of it.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision into which the list values should be read.
+ * @param listChunk
+ * indicating the lazy loading behavior: {@link CDORevision#UNCHUNKED} means that the whole list should be
+ * read. Else, if <code>listChunk >= 0</code>, the list is filled with {@link InternalCDOList#UNINITIALIZED}
+ * and only the first <code>listChunk</code> values are read.
+ */
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk);
+
+ /**
+ * Used to load-on-demand chunks of a list.
+ *
+ * @param dbStoreChunkReader
+ * the chunkReader to use
+ * @param chunks
+ * the chunks to read
+ * @param where
+ * the where-clause to use in order to read the chunks.
+ */
+ public void readChunks(IDBStoreChunkReader dbStoreChunkReader, List<Chunk> chunks, String where);
+
+ /**
+ * Hook with which a list mapping is notified that a containing object has been revised. Can be implemented in order
+ * to clean up lists of revised objects.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param id
+ * the ID of the object which has been revised.
+ * @param revised
+ * the timestamp at which the object was revised.
+ * @since 3.0
+ */
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised);
+
+ /**
+ * Retrieve cross-references from DB.
+ *
+ * @see IClassMapping#queryXRefs(IDBStoreAccessor, IStoreAccessor.QueryXRefsContext, String)
+ * @see IStoreAccessor#queryXRefs(IStoreAccessor.QueryXRefsContext)
+ * @since 4.0
+ */
+ public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
+ QueryXRefsContext context, String idString);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java
new file mode 100644
index 0000000000..5ed5698512
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+
+/**
+ * Interface to complement {@link IListMapping} in order to provide list delta processing support.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IListMappingDeltaSupport
+{
+ /**
+ * Process a set of CDOFeatureDeltas for a many-valued feature.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the ID of the revision affected
+ * @param oldVersion
+ * the original version of the revision
+ * @param newVersion
+ * the new revision of the revision (after the change)
+ * @param created
+ * the creation date for the new revision
+ * @param delta
+ * the {@link CDOListFeatureDelta} which contains the list deltas.
+ * @since 4.0
+ */
+ public void processDelta(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion,
+ long created, CDOListFeatureDelta delta);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java
new file mode 100644
index 0000000000..90c6516cc6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java
@@ -0,0 +1,348 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.internal.db.DBStore;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The mapping strategy acts as a connection between the DBStore and the database management (and OR-mapping) classes.
+ * The {@link DBStore} uses methods of this interface to create and lookup mappings (or mappers, as they could also be
+ * named as such) and to get properties and informations about the mappings used. The mapping classes (e.g., instances
+ * of IClassMapping and IListMapping) also use this class as a central point of information and as a resource of common
+ * functionalities.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IMappingStrategy
+{
+ /**
+ * Name of the integer property that configures the maximum length for table names. A value of zero indicates the
+ * value of the {@link IDBAdapter#getMaxTableNameLength() db adapter} to be used.
+ */
+ public static final String PROP_MAX_TABLE_NAME_LENGTH = "maxTableNameLength"; //$NON-NLS-1$
+
+ /**
+ * Name of the integer property that configures the maximum length for column names. A value of zero indicates the
+ * value of the {@link IDBAdapter#getMaxFieldNameLength() db adapter} to be used.
+ */
+ public static final String PROP_MAX_FIELD_NAME_LENGTH = "maxFieldNameLength"; //$NON-NLS-1$
+
+ /**
+ * Name of the String property that specifies a common prefix for table names.
+ */
+ public static final String PROP_TABLE_NAME_PREFIX = "tableNamePrefix"; //$NON-NLS-1$
+
+ /**
+ * Name of the boolean property that configures whether the table names are made of simple class names or of qualified
+ * class names.
+ */
+ public static final String PROP_QUALIFIED_NAMES = "qualifiedNames"; //$NON-NLS-1$
+
+ /**
+ * Name of the boolean property that configures whether table names and column names are always suffixed with the
+ * internal DBID or only in cases where generated names violate the naming constraints of the underlying backend.
+ */
+ public static final String PROP_FORCE_NAMES_WITH_ID = "forceNamesWithID"; //$NON-NLS-1$
+
+ /**
+ * Name of the integer property that configures the size of the object type in-memory cache. Possible configuration
+ * values are:
+ * <ul>
+ * <li>0 (zero). Don't use memory caching.
+ * <li>&gt;0. Use memory caching with the cache size given.
+ * </ul>
+ * Default is a memory cache size of 10,000,000.
+ * <p>
+ *
+ * @since 4.0
+ */
+ public static final String PROP_OBJECT_TYPE_CACHE_SIZE = "objectTypeCacheSize"; //$NON-NLS-1$
+
+ /**
+ * @return the store, this MappingStrategy instance belongs to.
+ */
+ public IDBStore getStore();
+
+ /**
+ * Set the store to which this MappingStrategy instance belongs. Should only be called by the {@link DBStore}, and
+ * only once to initialize the connection between {@link DBStore} and mapping strategy.
+ *
+ * @param dbStore
+ * the DBStore instance to which this MappingStrategy instance belongs.
+ */
+ public void setStore(IDBStore dbStore);
+
+ /**
+ * Factory for value mappings of single-valued attributes.
+ *
+ * @param feature
+ * the feature for which a mapping should be created. It must hold <code>feature.isMany() == false</code>.
+ * @return the mapping created.
+ */
+ public ITypeMapping createValueMapping(EStructuralFeature feature);
+
+ /**
+ * Factory for value mappings of multi-valued-attributes.
+ *
+ * @param containingClass
+ * the class containing the feature.
+ * @param feature
+ * the feature for which a mapping should be created. It must hold <code>feature.isMany() == true</code>.
+ */
+ public IListMapping createListMapping(EClass containingClass, EStructuralFeature feature);
+
+ /**
+ * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes.
+ *
+ * @param element
+ * the element for which the name should be created. It must hold:
+ * <code>element instanceof EClass || element instanceof EPackage</code>.
+ * @return the created table name. It is guaranteed that the table name is compatible with the chosen database.
+ */
+ public String getTableName(ENamedElement element);
+
+ /**
+ * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes.
+ * Should only be called by mapping classes.
+ *
+ * @param containingClass
+ * the class containeng the feature.
+ * @param feature
+ * the feature for which the table name should be created.
+ * @return the created table name. It is guaranteed that the table name is compatible with the chosen database.
+ */
+ public String getTableName(EClass containingClass, EStructuralFeature feature);
+
+ /**
+ * Create a suitable column name which can be used to map the given element. Should only be called by mapping classes.
+ *
+ * @param feature
+ * the feature for which the column name should be created.
+ * @return the created column name. It is guaranteed that the name is compatible with the chosen database.
+ */
+ public String getFieldName(EStructuralFeature feature);
+
+ /**
+ * Create and initialize the mapping infrastructure for the given packages. Should be called from the DBStore or the
+ * DBStoreAccessor.
+ *
+ * @param connection
+ * the connection to use.
+ * @param packageUnits
+ * the packages whose elements should be mapped.
+ * @param monitor
+ * the monitor to report progress.
+ */
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * Remove the mapping infrastructure for the given packages. Should be called from the DBStore or the DBStoreAccessor.
+ *
+ * @param connection
+ * the connection to use.
+ * @param packageUnits
+ * the packages for which the mappings should be removed
+ * @since 4.0
+ */
+ // Bugzilla 298632
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits);
+
+ /**
+ * Look up an existing class mapping for the given class. Before this method is called, the class mapping must have
+ * been initialized by calling {@link #createMapping(Connection, InternalCDOPackageUnit[], OMMonitor)} on its
+ * containing package.
+ *
+ * @param eClass
+ * the class to look up.
+ * @return the class mapping.
+ */
+ public IClassMapping getClassMapping(EClass eClass);
+
+ /**
+ * Returns all class mappings of this strategy.
+ *
+ * @since 4.0
+ */
+ public Map<EClass, IClassMapping> getClassMappings();
+
+ /**
+ * Returns all class mappings of this strategy.
+ *
+ * @since 4.0
+ */
+ public Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand);
+
+ /**
+ * Query if this mapping supports revision deltas. <br>
+ * If this method returns <code>true</code>, it is guaranteed that all class mappings returned by
+ * {@link #getClassMapping(EClass)} implement {@link IClassMappingDeltaSupport}.
+ *
+ * @return <code>true</code> if revision deltas are supported, <code>false</code> else.
+ */
+ public boolean hasDeltaSupport();
+
+ /**
+ * Query if this mapping supports audits. <br>
+ * If this method returns <code>true</code>, it is guaranteed that all class mappings returned by
+ * {@link #getClassMapping(EClass)} implement {@link IClassMappingAuditSupport}.
+ *
+ * @return <code>true</code> if audits are supported, <code>false</code> else.
+ */
+ public boolean hasAuditSupport();
+
+ /**
+ * Query if this mapping supports branches. <br>
+ *
+ * @return <code>true</code> if branches are supported, <code>false</code> else.
+ * @since 3.0
+ */
+ public boolean hasBranchingSupport();
+
+ /**
+ * Executes a resource query.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param context
+ * the context from which the query parameters are read and to which the result is written.
+ */
+ public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context);
+
+ /**
+ * Executes a cross reference query.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param context
+ * the context from which the query parameters are read and to which the result is written.
+ * @since 3.0
+ */
+ public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context);
+
+ /**
+ * Read the type (i.e. class) of the object referred to by a given ID.
+ *
+ * @param accessor
+ * the accessor to use to look up the type.
+ * @param id
+ * the ID of the object for which the type is to be determined.
+ * @return the type of the object.
+ */
+ public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ /**
+ * Get an iterator over all instances of objects in the store.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @return the iterator.
+ */
+ public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor);
+
+ /**
+ * Return the maximum object id used in the store. This is used by the DBStore if a previous crash is discovered
+ * during the startup process. Should only be called by the DBStore and only during startup.
+ *
+ * @param dbAdapter
+ * the dbAdapter to use to access the database
+ * @param connection
+ * the connection to use to access the database
+ * @since 4.0
+ */
+ public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection);
+
+ /**
+ * Returns the configuration properties of this mapping strategy.
+ *
+ * @since 4.0
+ */
+ public Map<String, String> getProperties();
+
+ /**
+ * Set configuration properties for this mapping strategy. Should only be called by the factory creating the mapping
+ * strategy instance.
+ *
+ * @param properties
+ * the configuration properties to set.
+ */
+ public void setProperties(Map<String, String> properties);
+
+ /**
+ * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following
+ * conditions are met:
+ * <ul>
+ * <li>The <code>eClass</code> parameter is <code>null</code> or equal to <code>revision.getEClass()</code>.
+ * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>.
+ * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to
+ * <code>revision.getTimeStamp()</code>.
+ * </ul>
+ *
+ * @since 4.0
+ */
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler);
+
+ /**
+ * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges.
+ * DetachedCDORevisions must also be considered!
+ *
+ * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...)
+ * @since 4.0
+ */
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments);
+
+ /**
+ * @since 3.0
+ */
+ public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int lastReplicatedBranchID, int lastBranchID,
+ long lastReplicatedCommitTime, long lastCommitTime) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public String getListJoin(String attrTable, String listTable);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
new file mode 100644
index 0000000000..c6d463c15e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
@@ -0,0 +1,278 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.factory.IFactory;
+
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+
+/**
+ * Mapping of single values to and from the database.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface ITypeMapping
+{
+ /**
+ * @return The feature which is associated with this mapping.
+ */
+ public EStructuralFeature getFeature();
+
+ /**
+ * @return The db field which is associated with this mapping.
+ */
+ public IDBField getField();
+
+ /**
+ * @return The db type which is associated with this mapping.
+ * @since 3.0
+ */
+ public DBType getDBType();
+
+ /**
+ * @since 4.0
+ */
+ public void setMappingStrategy(IMappingStrategy mappingStrategy);
+
+ /**
+ * @since 4.0
+ */
+ public void setFeature(EStructuralFeature feature);
+
+ /**
+ * @since 4.0
+ */
+ public void setDBType(DBType dbType);
+
+ /**
+ * Creates the DBField and adds it to the given table. The name of the DBField is derived from the feature.
+ *
+ * @param table
+ * the table to add this field to.
+ */
+ public void createDBField(IDBTable table);
+
+ /**
+ * Creates the DBField and adds it to the given table. The name of the DBField is explicitly determined by the
+ * corresponding parameter.
+ *
+ * @param table
+ * the table to add this field to.
+ * @param fieldName
+ * the name for the DBField.
+ */
+ public void createDBField(IDBTable table, String fieldName);
+
+ /**
+ * Sets the DBField. The name of the DBField is explicitly determined by the corresponding parameter.
+ *
+ * @param table
+ * the table to add this field to.
+ * @param fieldName
+ * the name for the DBField.
+ * @since 3.0
+ */
+ public void setDBField(IDBTable table, String fieldName);
+
+ /**
+ * Set the given value to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code> method.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the <code>setXxx</code> method.
+ * @param value
+ * the value to set.
+ * @throws SQLException
+ * if the <code>setXxx</code> throws it.
+ */
+ public void setValue(PreparedStatement stmt, int index, Object value) throws SQLException;
+
+ /**
+ * Set the feature's default value to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code>
+ * method.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the <code>setXxx</code> method.
+ * @throws SQLException
+ * if the <code>setXxx</code> throws it.
+ * @since 3.0
+ */
+ public void setDefaultValue(PreparedStatement stmt, int index) throws SQLException;
+
+ /**
+ * Set a value of the given revision to the JDBC {@link PreparedStatement} using an appropriate <code>setXxx</code>
+ * method. The feature from which the value is taken is determined by {@link #getFeature()}.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the <code>setXxx</code> method.
+ * @param value
+ * the revision to get the value to set from.
+ * @throws SQLException
+ * if the <code>setXxx</code> throws it.
+ */
+ public void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision value) throws SQLException;
+
+ /**
+ * Read the value from a {@link ResultSet} and convert it from the DB to the CDO representation. The resultSet field
+ * to read from is determined automatically by the internal {@link #getField()} name.
+ *
+ * @param resultSet
+ * the result set to read from
+ * @return the read value
+ * @throws SQLException
+ * if reading the value throws an SQLException
+ * @since 3.0
+ */
+ public Object readValue(ResultSet resultSet) throws SQLException;
+
+ /**
+ * Read a value from a {@link ResultSet}, convert it from the DB to the CDO representation and set it to the feature
+ * of the revision. The feature is determined by getFeature() The resultSet field to read from is determined
+ * automatically by the internal {@link #getField()} name.
+ *
+ * @param resultSet
+ * the result set to read from
+ * @param revision
+ * the revision to which the value should be set.
+ * @throws SQLException
+ * if reading the value throws an SQLException
+ * @since 3.0
+ */
+ public void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException;
+
+ /**
+ * A descriptor which describes one type mapping class. The descriptor is encoded in the factoryType which is used as
+ * a string description for the extension point mechanism. Translations and instantiations can be done using the
+ * methods in {@link TypeMappingUtil}.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Descriptor
+ {
+ /**
+ * The factoryType of the factory which can create the type mapping
+ */
+ public String getFactoryType();
+
+ /**
+ * The ID of the described type mapping.
+ */
+ public String getID();
+
+ /**
+ * The source (i.e., model) type that can be mapped by the type mapping.
+ */
+ public EClassifier getEClassifier();
+
+ /**
+ * The target (i.e., db) type that can be mapped by the type mapping.
+ */
+ public DBType getDBType();
+
+ }
+
+ /**
+ * A global (singleton) registry which collects all available type mappings which are either available in the CDO
+ * core, as declared extensions, or registered manually.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Registry
+ {
+ /**
+ * The one global (singleton) registry instance.
+ */
+ public static Registry INSTANCE = new TypeMappingRegistry();
+
+ /**
+ * Register a type mapping by descriptor.
+ */
+ public void registerTypeMapping(ITypeMapping.Descriptor descriptor);
+
+ /**
+ * Provides a list of all DBTypes for which type mappings exist in the registry. This is used in feature map tables
+ * to create columns for all of these types.
+ */
+ public Collection<DBType> getDefaultFeatureMapDBTypes();
+ }
+
+ /**
+ * A provider for type mapping information. This provider is used by the {@link TypeMappingRegistry} to create an
+ * {@link ITypeMapping} instance suitable for a given feature and DB field. Usually, one factory is responsible for
+ * one type mapping.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Provider
+ {
+ /**
+ * The one global (singleton) provider instance.
+ */
+ public static Provider INSTANCE = (Provider)Registry.INSTANCE;
+
+ /**
+ * Create an {@link ITypeMapping} implementation.
+ *
+ * @param mappingStrategy
+ * the mapping strategy
+ * @param feature
+ * the feature the new type mapping shall be responsible for
+ * @return the newly created {@link ITypeMapping} instance
+ */
+ public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature);
+ }
+
+ /**
+ * A factory for typeMappings. This is a regular Net4j factory registered by the respective extension point. It
+ * enhances the regular factory using a descriptor which is translated from and to the factoryType by the methods in
+ * {@link TypeMappingUtil}.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Factory extends IFactory
+ {
+ /**
+ * The Net4j factory product group for type mappings
+ */
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.db.typeMappings";
+
+ /**
+ * Return the descriptor of the kind of type mapping created by this factory.
+ */
+ public ITypeMapping.Descriptor getDescriptor();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java
new file mode 100644
index 0000000000..cec7a9382a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with mapping strategies and mappings for classes, lists and types.
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java
new file mode 100644
index 0000000000..53e53996bb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with DB stores and accessors.
+ */
+package org.eclipse.emf.cdo.server.db;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java
new file mode 100644
index 0000000000..2ffd031698
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+import java.sql.Connection;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public abstract class AbstractPreparedStatementCache extends Lifecycle implements IPreparedStatementCache
+{
+ private Connection connection;
+
+ public AbstractPreparedStatementCache()
+ {
+ }
+
+ public final Connection getConnection()
+ {
+ return connection;
+ }
+
+ public final void setConnection(Connection connection)
+ {
+ checkInactive();
+ this.connection = connection;
+ }
+
+ @Override
+ protected void doBeforeActivate()
+ {
+ checkState(connection, "Must have valid connection to start"); //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
new file mode 100644
index 0000000000..dc3e19b6db
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ * Andre Dietisheim - bug 256649
+ *
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.spi.db.DBSchema;
+
+/**
+ * @author Eike Stepper
+ */
+public class CDODBSchema extends DBSchema
+{
+ public static final CDODBSchema INSTANCE = new CDODBSchema();
+
+ /**
+ * DBTable cdo_properties
+ */
+ public static final IDBTable PROPERTIES = INSTANCE.addTable("cdo_properties"); //$NON-NLS-1$
+
+ public static final IDBField PROPERTIES_NAME = //
+ PROPERTIES.addField("name", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PROPERTIES_VALUE = //
+ PROPERTIES.addField("value", DBType.LONGVARCHAR); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PROPERTIES_PK = //
+ PROPERTIES.addIndex(IDBIndex.Type.PRIMARY_KEY, PROPERTIES_NAME);
+
+ public static final String SQL_DELETE_PROPERTIES = "DELETE FROM " + PROPERTIES + " WHERE " + PROPERTIES_NAME + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_INSERT_PROPERTIES = "INSERT INTO " + PROPERTIES + " (" + PROPERTIES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + PROPERTIES_VALUE + ") VALUES (?, ?)"; //$NON-NLS-1$
+
+ public static final String SQL_SELECT_PROPERTIES = "SELECT " + PROPERTIES_VALUE + " FROM " + PROPERTIES + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + PROPERTIES_NAME + "=?"; //$NON-NLS-1$
+
+ public static final String SQL_SELECT_ALL_PROPERTIES = "SELECT " + PROPERTIES_NAME + ", " + PROPERTIES_VALUE //$NON-NLS-1$ //$NON-NLS-2$
+ + " FROM " + PROPERTIES; //$NON-NLS-1$
+
+ /**
+ * DBTable cdo_package_units
+ */
+ public static final IDBTable PACKAGE_UNITS = INSTANCE.addTable("cdo_package_units"); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_ID = //
+ PACKAGE_UNITS.addField("id", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_ORIGINAL_TYPE = //
+ PACKAGE_UNITS.addField("original_type", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_TIME_STAMP = //
+ PACKAGE_UNITS.addField("time_stamp", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_PACKAGE_DATA = //
+ PACKAGE_UNITS.addField("package_data", DBType.BLOB); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PACKAGE_UNITS_PK = //
+ PACKAGE_UNITS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_UNITS_ID);
+
+ /**
+ * DBTable cdo_packages
+ */
+ public static final IDBTable PACKAGE_INFOS = INSTANCE.addTable("cdo_package_infos"); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_URI = //
+ PACKAGE_INFOS.addField("uri", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_PARENT = //
+ PACKAGE_INFOS.addField("parent", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_UNIT = //
+ PACKAGE_INFOS.addField("unit", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_PK = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_INFOS_URI);
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_PARENT = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_PARENT);
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_UNIT = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_UNIT);
+
+ /**
+ * DBTable cdo_branches
+ */
+ public static final IDBTable BRANCHES = INSTANCE.addTable("cdo_branches"); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_ID = //
+ BRANCHES.addField("id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_NAME = //
+ BRANCHES.addField("name", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_BASE_BRANCH_ID = //
+ BRANCHES.addField("base_id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_BASE_TIMESTAMP = //
+ BRANCHES.addField("base_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_BRANCHES_ID = //
+ BRANCHES.addIndex(IDBIndex.Type.PRIMARY_KEY, BRANCHES_ID);
+
+ public static final String SQL_CREATE_BRANCH = "INSERT INTO " + BRANCHES + " (" + BRANCHES_ID + ", " + BRANCHES_NAME //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + ", " + BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP + ") VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_BRANCH = "SELECT " + BRANCHES_NAME + ", " + BRANCHES_BASE_BRANCH_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_SUB_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_BASE_BRANCH_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP //$NON-NLS-1$
+ + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + " BETWEEN ? AND ? ORDER BY " + BRANCHES_ID; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ /**
+ * DBTable cdo_commit_infos
+ */
+ public static final IDBTable COMMIT_INFOS = INSTANCE.addTable("cdo_commit_infos"); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_TIMESTAMP = //
+ COMMIT_INFOS.addField("commit_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_PREVIOUS_TIMESTAMP = //
+ COMMIT_INFOS.addField("previous_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_BRANCH = //
+ COMMIT_INFOS.addField("branch_id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_USER = //
+ COMMIT_INFOS.addField("user_id", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_COMMENT = //
+ COMMIT_INFOS.addField("commit_comment", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_PK = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, COMMIT_INFOS_TIMESTAMP);
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_BRANCH = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_BRANCH);
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_USER = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_USER);
+
+ public static final String SQL_CREATE_COMMIT_INFO = "INSERT INTO " + COMMIT_INFOS + "(" + COMMIT_INFOS_TIMESTAMP //$NON-NLS-1$ //$NON-NLS-2$
+ + ", " + COMMIT_INFOS_PREVIOUS_TIMESTAMP + ", " + COMMIT_INFOS_BRANCH + ", " + COMMIT_INFOS_USER + ", " + COMMIT_INFOS_COMMENT + ") " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ + "VALUES (?, ?, ?, ?, ?)"; //$NON-NLS-1$
+
+ /**
+ * DBTable cdo_lobs
+ */
+ public static final IDBTable LOBS = INSTANCE.addTable("cdo_lobs"); //$NON-NLS-1$
+
+ public static final IDBField LOBS_ID = //
+ LOBS.addField("id", DBType.VARCHAR, 64); //$NON-NLS-1$
+
+ public static final IDBField LOBS_SIZE = //
+ LOBS.addField("size", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField LOBS_BDATA = //
+ LOBS.addField("bdata", DBType.BLOB); //$NON-NLS-1$
+
+ public static final IDBField LOBS_CDATA = //
+ LOBS.addField("cdata", DBType.CLOB); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_LOBS_ID = //
+ LOBS.addIndex(IDBIndex.Type.PRIMARY_KEY, LOBS_ID);
+
+ public static final String SQL_QUERY_LOBS = "SELECT 1 FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_HANDLE_LOBS = "SELECT " + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ public static final String SQL_LOAD_LOB = "SELECT " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+
+ public static final String SQL_WRITE_BLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ public static final String SQL_WRITE_CLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_CDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ /**
+ * Name of object table
+ */
+ public static final String CDO_OBJECTS = "cdo_objects"; //$NON-NLS-1$
+
+ /**
+ * Field names of attribute tables
+ */
+ public static final String ATTRIBUTES_ID = "cdo_id"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CLASS = "cdo_class"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CREATED = "cdo_created"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_REVISED = "cdo_revised"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_RESOURCE = "cdo_resource"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CONTAINER = "cdo_container"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_FEATURE = "cdo_feature"; //$NON-NLS-1$
+
+ /**
+ * Field names of list tables
+ */
+ public static final String LIST_FEATURE = "cdo_feature"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_ID = "cdo_source"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String LIST_IDX = "cdo_idx"; //$NON-NLS-1$
+
+ public static final String LIST_VALUE = "cdo_value"; //$NON-NLS-1$
+
+ /**
+ * Field names of featuremap tables
+ */
+ public static final String FEATUREMAP_REVISION_ID = "cdo_id"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_IDX = "cdo_idx"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_TAG = "cdo_tag"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VALUE = "cdo_value"; //$NON-NLS-1$
+
+ private CDODBSchema()
+ {
+ super("CDO"); //$NON-NLS-1$
+ }
+
+ static
+ {
+ INSTANCE.lock();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java
new file mode 100644
index 0000000000..148115658f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kai Schlamp - initial API and implementation
+ * Eike Stepper - maintenance
+ * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping
+ * Stefan Winkler - maintenance
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+
+/**
+ * @author Kai Schlamp
+ */
+public enum DBAnnotation
+{
+ TABLE_MAPPING("tableMapping"), //
+ TABLE_NAME("tableName"), //
+ COLUMN_NAME("columnName"), //
+ COLUMN_TYPE("columnType"), //
+ COLUMN_LENGTH("columnLength"), //
+ TYPE_MAPPING("typeMapping");
+
+ public final static String SOURCE_URI = "http://www.eclipse.org/CDO/DBStore";
+
+ public final static String TABLE_MAPPING_NONE = "NONE";
+
+ private String keyword;
+
+ private DBAnnotation(String keyword)
+ {
+ this.keyword = keyword;
+ }
+
+ public String getKeyword()
+ {
+ return keyword == null ? super.toString() : keyword;
+ }
+
+ /**
+ * @return A non-empty string or <code>null</code>.
+ */
+ public String getValue(EModelElement element)
+ {
+ String value = EcoreUtil.getAnnotation(element, SOURCE_URI, keyword);
+ if (value != null && value.length() == 0)
+ {
+ return null;
+ }
+
+ return value;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getKeyword();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java
new file mode 100644
index 0000000000..c81aafe6ab
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.CDOServerBrowser;
+import org.eclipse.emf.cdo.server.CDOServerBrowser.AbstractPage;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import java.io.PrintStream;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class DBBrowserPage extends AbstractPage
+{
+ public DBBrowserPage()
+ {
+ super("tables", "Database Tables");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return repository.getStore() instanceof IDBConnectionProvider;
+ }
+
+ public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out)
+ {
+ IDBConnectionProvider connectionProvider = (IDBConnectionProvider)repository.getStore();
+ Connection connection = null;
+
+ try
+ {
+ connection = connectionProvider.getConnection();
+
+ out.print("<table border=\"0\">\r\n");
+ out.print("<tr>\r\n");
+
+ out.print("<td valign=\"top\">\r\n");
+ String table = showTables(browser, out, connection, repository.getName());
+ out.print("</td>\r\n");
+
+ if (table != null)
+ {
+ out.print("<td valign=\"top\">\r\n");
+ showTable(browser, out, connection, table);
+ out.print("</td>\r\n");
+ }
+
+ out.print("</tr>\r\n");
+ out.print("</table>\r\n");
+ }
+ catch (DBException ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
+ DBUtil.close(connection);
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected String showTables(CDOServerBrowser browser, PrintStream pout, Connection connection, String repo)
+ {
+ String table = browser.getParam("table");
+
+ List<String> allTableNames = DBUtil.getAllTableNames(connection, repo);
+ for (String tableName : allTableNames)
+ {
+ if (table == null)
+ {
+ table = tableName;
+ }
+
+ String label = browser.escape(tableName)/* .toLowerCase() */;
+ if (tableName.equals(table))
+ {
+ pout.print("<b>" + label + "</b><br>\r\n");
+ }
+ else
+ {
+ pout.print(browser.href(label, getName(), "table", tableName, "order", null, "direction", null) + "<br>\r\n");
+ }
+ }
+
+ return table;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void showTable(CDOServerBrowser browser, PrintStream pout, Connection connection, String table)
+ {
+ try
+ {
+ String order = browser.getParam("order");
+ executeQuery(browser, pout, connection, "SELECT * FROM " + table
+ + (order == null ? "" : " ORDER BY " + order + " " + browser.getParam("direction")));
+ }
+ catch (Exception ex)
+ {
+ browser.removeParam("order");
+ browser.removeParam("direction");
+ executeQuery(browser, pout, connection, "SELECT * FROM " + table);
+ }
+ }
+
+ protected void executeQuery(CDOServerBrowser browser, PrintStream pout, Connection connection, String sql)
+ {
+ String order = browser.getParam("order");
+ String direction = browser.getParam("direction");
+ String highlight = browser.getParam("highlight");
+
+ Statement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = connection.createStatement();
+ resultSet = stmt.executeQuery(sql);
+
+ ResultSetMetaData metaData = resultSet.getMetaData();
+ int columns = metaData.getColumnCount();
+
+ pout.print("<table border=\"1\" cellpadding=\"2\">\r\n");
+ pout.print("<tr>\r\n");
+ pout.print("<td>&nbsp;</td>\r\n");
+ for (int i = 0; i < columns; i++)
+ {
+ String column = metaData.getColumnLabel(1 + i);
+ String type = metaData.getColumnTypeName(1 + i).toLowerCase();
+
+ String dir = column.equals(order) && "ASC".equals(direction) ? "DESC" : "ASC";
+ pout.print("<td align=\"center\"><b>" + browser.href(column, getName(), "order", column, "direction", dir));
+ pout.print("</b><br>" + type + "</td>\r\n");
+ }
+
+ pout.print("</tr>\r\n");
+
+ int row = 0;
+ while (resultSet.next())
+ {
+ ++row;
+ pout.print("<tr>\r\n");
+ pout.print("<td><b>" + row + "</b></td>\r\n");
+ for (int i = 0; i < columns; i++)
+ {
+ String value = resultSet.getString(1 + i);
+ String bgcolor = highlight != null && highlight.equals(value) ? " bgcolor=\"#fffca6\"" : "";
+ pout.print("<td" + bgcolor + ">" + browser.href(value, getName(), "highlight", value) + "</td>\r\n");
+ }
+
+ pout.print("</tr>\r\n");
+ }
+
+ pout.print("</table>\r\n");
+ }
+ catch (SQLException ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmt);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends org.eclipse.net4j.util.factory.Factory
+ {
+ public static final String TYPE = "default";
+
+ public Factory()
+ {
+ super(PRODUCT_GROUP, TYPE);
+ }
+
+ public DBBrowserPage create(String description) throws ProductCreationException
+ {
+ return new DBBrowserPage();
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java
new file mode 100644
index 0000000000..d47db43967
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBRevisionHandler.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision;
+
+/**
+ * @author Eike Stepper
+ */
+public class DBRevisionHandler implements CDORevisionHandler
+{
+ private CDORevisionHandler delegate;
+
+ public DBRevisionHandler(CDORevisionHandler delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ public boolean handleRevision(CDORevision revision)
+ {
+ if (revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1)
+ {
+ revision = new DetachedCDORevision(revision.getEClass(), revision.getID(), revision.getBranch(),
+ -revision.getVersion(), revision.getTimeStamp(), revision.getRevised());
+ }
+
+ return delegate.handleRevision(revision);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java
new file mode 100644
index 0000000000..3917333db8
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java
@@ -0,0 +1,727 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 259402
+ * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation)
+ * Stefan Winkler - Bug 289056: [DB] Exception "ERROR: relation "cdo_external_refs" does not exist" while executing test-suite for PostgreSQL
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.IView;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.messages.Messages;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor;
+import org.eclipse.emf.cdo.spi.server.Store;
+import org.eclipse.emf.cdo.spi.server.StoreAccessorPool;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.spi.db.DBSchema;
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.ProgressDistributor;
+
+import javax.sql.DataSource;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Timer;
+
+/**
+ * @author Eike Stepper
+ */
+public class DBStore extends Store implements IDBStore, CDOAllRevisionsProvider
+{
+ public static final String TYPE = "db"; //$NON-NLS-1$
+
+ private static final String PROP_REPOSITORY_CREATED = "org.eclipse.emf.cdo.server.db.repositoryCreated"; //$NON-NLS-1$
+
+ private static final String PROP_REPOSITORY_STOPPED = "org.eclipse.emf.cdo.server.db.repositoryStopped"; //$NON-NLS-1$
+
+ private static final String PROP_NEXT_LOCAL_CDOID = "org.eclipse.emf.cdo.server.db.nextLocalCDOID"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_CDOID = "org.eclipse.emf.cdo.server.db.lastCDOID"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_BRANCHID = "org.eclipse.emf.cdo.server.db.lastBranchID"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_LOCAL_BRANCHID = "org.eclipse.emf.cdo.server.db.lastLocalBranchID"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastCommitTime"; //$NON-NLS-1$
+
+ private static final String PROP_LAST_NONLOCAL_COMMITTIME = "org.eclipse.emf.cdo.server.db.lastNonLocalCommitTime"; //$NON-NLS-1$
+
+ private static final String PROP_GRACEFULLY_SHUT_DOWN = "org.eclipse.emf.cdo.server.db.gracefullyShutDown"; //$NON-NLS-1$
+
+ private long creationTime;
+
+ private boolean firstTime;
+
+ private Map<String, String> properties;
+
+ private IIDHandler idHandler;
+
+ private IMetaDataManager metaDataManager = new MetaDataManager(this);
+
+ private DurableLockingManager durableLockingManager = new DurableLockingManager(this);
+
+ private IMappingStrategy mappingStrategy;
+
+ private IDBSchema dbSchema;
+
+ private IDBAdapter dbAdapter;
+
+ private IDBConnectionProvider dbConnectionProvider;
+
+ @ExcludeFromDump
+ private transient ProgressDistributor accessorWriteDistributor = new ProgressDistributor.Geometric()
+ {
+ @Override
+ public String toString()
+ {
+ String result = "accessorWriteDistributor"; //$NON-NLS-1$
+ if (getRepository() != null)
+ {
+ result += ": " + getRepository().getName(); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+ };
+
+ @ExcludeFromDump
+ private transient StoreAccessorPool readerPool = new StoreAccessorPool(this, null);
+
+ @ExcludeFromDump
+ private transient StoreAccessorPool writerPool = new StoreAccessorPool(this, null);
+
+ @ExcludeFromDump
+ private transient Timer connectionKeepAliveTimer;
+
+ public DBStore()
+ {
+ super(TYPE, null, set(ChangeFormat.REVISION, ChangeFormat.DELTA), //
+ set(RevisionTemporality.AUDITING, RevisionTemporality.NONE), //
+ set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING));
+ }
+
+ public IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public void setMappingStrategy(IMappingStrategy mappingStrategy)
+ {
+ this.mappingStrategy = mappingStrategy;
+ mappingStrategy.setStore(this);
+ }
+
+ public IDBAdapter getDBAdapter()
+ {
+ return dbAdapter;
+ }
+
+ public void setDBAdapter(IDBAdapter dbAdapter)
+ {
+ this.dbAdapter = dbAdapter;
+ }
+
+ public void setProperties(Map<String, String> properties)
+ {
+ this.properties = properties;
+ }
+
+ public Map<String, String> getProperties()
+ {
+ return properties;
+ }
+
+ public IIDHandler getIDHandler()
+ {
+ return idHandler;
+ }
+
+ public Connection getConnection()
+ {
+ Connection connection = dbConnectionProvider.getConnection();
+ if (connection == null)
+ {
+ throw new DBException("No connection from connection provider: " + dbConnectionProvider); //$NON-NLS-1$
+ }
+
+ try
+ {
+ connection.setAutoCommit(false);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex, "SET AUTO COMMIT = false");
+ }
+
+ return connection;
+ }
+
+ public void setDbConnectionProvider(IDBConnectionProvider dbConnectionProvider)
+ {
+ this.dbConnectionProvider = dbConnectionProvider;
+ }
+
+ public void setDataSource(DataSource dataSource)
+ {
+ dbConnectionProvider = DBUtil.createConnectionProvider(dataSource);
+ }
+
+ public IMetaDataManager getMetaDataManager()
+ {
+ return metaDataManager;
+ }
+
+ public DurableLockingManager getDurableLockingManager()
+ {
+ return durableLockingManager;
+ }
+
+ public Timer getConnectionKeepAliveTimer()
+ {
+ return connectionKeepAliveTimer;
+ }
+
+ @Override
+ public Set<ChangeFormat> getSupportedChangeFormats()
+ {
+ if (mappingStrategy.hasDeltaSupport())
+ {
+ return set(ChangeFormat.DELTA);
+ }
+
+ return set(ChangeFormat.REVISION);
+ }
+
+ public ProgressDistributor getAccessorWriteDistributor()
+ {
+ return accessorWriteDistributor;
+ }
+
+ public IDBSchema getDBSchema()
+ {
+ return dbSchema;
+ }
+
+ public Map<String, String> getPersistentProperties(Set<String> names)
+ {
+ Connection connection = null;
+ PreparedStatement selectStmt = null;
+ String sql = null;
+
+ try
+ {
+ connection = getConnection();
+ Map<String, String> result = new HashMap<String, String>();
+ boolean allProperties = names == null || names.isEmpty();
+ if (allProperties)
+ {
+ sql = CDODBSchema.SQL_SELECT_ALL_PROPERTIES;
+ selectStmt = connection.prepareStatement(sql);
+ ResultSet resultSet = null;
+
+ try
+ {
+ resultSet = selectStmt.executeQuery();
+ while (resultSet.next())
+ {
+ String key = resultSet.getString(1);
+ String value = resultSet.getString(2);
+ result.put(key, value);
+ }
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+ else
+ {
+ sql = CDODBSchema.SQL_SELECT_PROPERTIES;
+ selectStmt = connection.prepareStatement(sql);
+ for (String name : names)
+ {
+ selectStmt.setString(1, name);
+ ResultSet resultSet = null;
+
+ try
+ {
+ resultSet = selectStmt.executeQuery();
+ if (resultSet.next())
+ {
+ String value = resultSet.getString(1);
+ result.put(name, value);
+ }
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+ }
+
+ return result;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex, sql);
+ }
+ finally
+ {
+ DBUtil.close(selectStmt);
+ DBUtil.close(connection);
+ }
+ }
+
+ public void setPersistentProperties(Map<String, String> properties)
+ {
+ Connection connection = null;
+ PreparedStatement deleteStmt = null;
+ PreparedStatement insertStmt = null;
+ String sql = null;
+
+ try
+ {
+ connection = getConnection();
+ deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES);
+ insertStmt = connection.prepareStatement(CDODBSchema.SQL_INSERT_PROPERTIES);
+
+ for (Entry<String, String> entry : properties.entrySet())
+ {
+ String name = entry.getKey();
+ String value = entry.getValue();
+
+ sql = CDODBSchema.SQL_DELETE_PROPERTIES;
+ deleteStmt.setString(1, name);
+ deleteStmt.executeUpdate();
+
+ sql = CDODBSchema.SQL_INSERT_PROPERTIES;
+ insertStmt.setString(1, name);
+ insertStmt.setString(2, value);
+ insertStmt.executeUpdate();
+ }
+
+ sql = null;
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex, sql);
+ }
+ finally
+ {
+ DBUtil.close(insertStmt);
+ DBUtil.close(deleteStmt);
+ DBUtil.close(connection);
+ }
+ }
+
+ public void removePersistentProperties(Set<String> names)
+ {
+ Connection connection = null;
+ PreparedStatement deleteStmt = null;
+
+ try
+ {
+ connection = getConnection();
+ deleteStmt = connection.prepareStatement(CDODBSchema.SQL_DELETE_PROPERTIES);
+
+ for (String name : names)
+ {
+ deleteStmt.setString(1, name);
+ deleteStmt.executeUpdate();
+ }
+
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex, CDODBSchema.SQL_DELETE_PROPERTIES);
+ }
+ finally
+ {
+ DBUtil.close(deleteStmt);
+ DBUtil.close(connection);
+ }
+ }
+
+ @Override
+ public DBStoreAccessor getReader(ISession session)
+ {
+ return (DBStoreAccessor)super.getReader(session);
+ }
+
+ @Override
+ public DBStoreAccessor getWriter(ITransaction transaction)
+ {
+ return (DBStoreAccessor)super.getWriter(transaction);
+ }
+
+ @Override
+ protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing)
+ {
+ return readerPool;
+ }
+
+ @Override
+ protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing)
+ {
+ return writerPool;
+ }
+
+ @Override
+ protected DBStoreAccessor createReader(ISession session) throws DBException
+ {
+ return new DBStoreAccessor(this, session);
+ }
+
+ @Override
+ protected DBStoreAccessor createWriter(ITransaction transaction) throws DBException
+ {
+ return new DBStoreAccessor(this, transaction);
+ }
+
+ public Map<CDOBranch, List<CDORevision>> getAllRevisions()
+ {
+ final Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>();
+ IDBStoreAccessor accessor = getReader(null);
+ StoreThreadLocal.setAccessor(accessor);
+
+ try
+ {
+ accessor.handleRevisions(null, null, CDOBranchPoint.UNSPECIFIED_DATE, true,
+ new CDORevisionHandler.Filtered.Undetached(new CDORevisionHandler()
+ {
+ public boolean handleRevision(CDORevision revision)
+ {
+ CDOBranch branch = revision.getBranch();
+ List<CDORevision> list = result.get(branch);
+ if (list == null)
+ {
+ list = new ArrayList<CDORevision>();
+ result.put(branch, list);
+ }
+
+ list.add(revision);
+ return true;
+ }
+ }));
+ }
+ finally
+ {
+ StoreThreadLocal.release();
+ }
+
+ return result;
+ }
+
+ public CDOID createObjectID(String val)
+ {
+ return idHandler.createCDOID(val);
+ }
+
+ public boolean isLocal(CDOID id)
+ {
+ return idHandler.isLocalCDOID(id);
+ }
+
+ public CDOID getNextCDOID(LongIDStoreAccessor accessor, CDORevision revision)
+ {
+ return idHandler.getNextCDOID(revision);
+ }
+
+ public long getCreationTime()
+ {
+ return creationTime;
+ }
+
+ public void setCreationTime(long creationTime)
+ {
+ this.creationTime = creationTime;
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(PROP_REPOSITORY_CREATED, Long.toString(creationTime));
+ setPersistentProperties(map);
+ }
+
+ public boolean isFirstStart()
+ {
+ return firstTime;
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkNull(mappingStrategy, Messages.getString("DBStore.2")); //$NON-NLS-1$
+ checkNull(dbAdapter, Messages.getString("DBStore.1")); //$NON-NLS-1$
+ checkNull(dbConnectionProvider, Messages.getString("DBStore.0")); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT)
+ {
+ idHandler = new UUIDHandler(this);
+ }
+ else
+ {
+ idHandler = new LongIDHandler(this);
+ }
+
+ setObjectIDTypes(idHandler.getObjectIDTypes());
+ connectionKeepAliveTimer = new Timer("Connection-Keep-Alive-" + this); //$NON-NLS-1$
+
+ Set<IDBTable> createdTables = null;
+ Connection connection = getConnection();
+
+ try
+ {
+ if (isDropAllDataOnActivate())
+ {
+ OM.LOG.info("Dropping all tables from repository " + getRepository().getName() + "...");
+ DBUtil.dropAllTables(connection, null);
+ connection.commit();
+ }
+
+ createdTables = CDODBSchema.INSTANCE.create(dbAdapter, connection);
+ connection.commit();
+ }
+ finally
+ {
+ DBUtil.close(connection);
+ }
+
+ dbSchema = createSchema();
+
+ LifecycleUtil.activate(idHandler);
+ LifecycleUtil.activate(metaDataManager);
+ LifecycleUtil.activate(durableLockingManager);
+ LifecycleUtil.activate(mappingStrategy);
+
+ setRevisionTemporality(mappingStrategy.hasAuditSupport() ? RevisionTemporality.AUDITING : RevisionTemporality.NONE);
+ setRevisionParallelism(mappingStrategy.hasBranchingSupport() ? RevisionParallelism.BRANCHING
+ : RevisionParallelism.NONE);
+
+ if (isFirstStart(createdTables))
+ {
+ firstStart();
+ }
+ else
+ {
+ reStart();
+ }
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(mappingStrategy);
+ LifecycleUtil.deactivate(durableLockingManager);
+ LifecycleUtil.deactivate(metaDataManager);
+ LifecycleUtil.deactivate(idHandler);
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(PROP_GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString());
+ map.put(PROP_REPOSITORY_STOPPED, Long.toString(getRepository().getTimeStamp()));
+
+ if (getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ map.put(PROP_NEXT_LOCAL_CDOID, Store.idToString(idHandler.getNextLocalObjectID()));
+ map.put(PROP_LAST_CDOID, Store.idToString(idHandler.getLastObjectID()));
+ }
+
+ map.put(PROP_LAST_BRANCHID, Integer.toString(getLastBranchID()));
+ map.put(PROP_LAST_LOCAL_BRANCHID, Integer.toString(getLastLocalBranchID()));
+ map.put(PROP_LAST_COMMITTIME, Long.toString(getLastCommitTime()));
+ map.put(PROP_LAST_NONLOCAL_COMMITTIME, Long.toString(getLastNonLocalCommitTime()));
+ setPersistentProperties(map);
+
+ if (readerPool != null)
+ {
+ readerPool.dispose();
+ }
+
+ if (writerPool != null)
+ {
+ writerPool.dispose();
+ }
+
+ connectionKeepAliveTimer.cancel();
+ connectionKeepAliveTimer = null;
+
+ super.doDeactivate();
+ }
+
+ protected boolean isFirstStart(Set<IDBTable> createdTables)
+ {
+ if (createdTables.contains(CDODBSchema.PROPERTIES))
+ {
+ return true;
+ }
+
+ Set<String> names = new HashSet<String>();
+ names.add(PROP_REPOSITORY_CREATED);
+
+ Map<String, String> map = getPersistentProperties(names);
+ return map.get(PROP_REPOSITORY_CREATED) == null;
+ }
+
+ protected void firstStart()
+ {
+ InternalRepository repository = getRepository();
+ setCreationTime(repository.getTimeStamp());
+ firstTime = true;
+ }
+
+ protected void reStart()
+ {
+ Set<String> names = new HashSet<String>();
+ names.add(PROP_REPOSITORY_CREATED);
+ names.add(PROP_GRACEFULLY_SHUT_DOWN);
+
+ Map<String, String> map = getPersistentProperties(names);
+ creationTime = Long.valueOf(map.get(PROP_REPOSITORY_CREATED));
+
+ if (map.containsKey(PROP_GRACEFULLY_SHUT_DOWN))
+ {
+ names.clear();
+
+ InternalRepository repository = getRepository();
+ boolean generatingIDs = repository.getIDGenerationLocation() == IDGenerationLocation.STORE;
+ if (generatingIDs)
+ {
+ names.add(PROP_NEXT_LOCAL_CDOID);
+ names.add(PROP_LAST_CDOID);
+ }
+
+ names.add(PROP_LAST_BRANCHID);
+ names.add(PROP_LAST_LOCAL_BRANCHID);
+ names.add(PROP_LAST_COMMITTIME);
+ names.add(PROP_LAST_NONLOCAL_COMMITTIME);
+ map = getPersistentProperties(names);
+
+ if (generatingIDs)
+ {
+ idHandler.setNextLocalObjectID(Store.stringToID(map.get(PROP_NEXT_LOCAL_CDOID)));
+ idHandler.setLastObjectID(Store.stringToID(map.get(PROP_LAST_CDOID)));
+ }
+
+ setLastBranchID(Integer.valueOf(map.get(PROP_LAST_BRANCHID)));
+ setLastLocalBranchID(Integer.valueOf(map.get(PROP_LAST_LOCAL_BRANCHID)));
+ setLastCommitTime(Long.valueOf(map.get(PROP_LAST_COMMITTIME)));
+ setLastNonLocalCommitTime(Long.valueOf(map.get(PROP_LAST_NONLOCAL_COMMITTIME)));
+ }
+ else
+ {
+ repairAfterCrash();
+ }
+
+ removePersistentProperties(Collections.singleton(PROP_GRACEFULLY_SHUT_DOWN));
+ }
+
+ protected void repairAfterCrash()
+ {
+ String name = getRepository().getName();
+ OM.LOG.warn(MessageFormat.format(Messages.getString("DBStore.9"), name)); //$NON-NLS-1$
+
+ Connection connection = getConnection();
+
+ try
+ {
+ connection.setAutoCommit(false);
+ connection.setReadOnly(true);
+
+ mappingStrategy.repairAfterCrash(dbAdapter, connection); // Must update the idHandler
+
+ boolean storeIDs = getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE;
+ CDOID lastObjectID = storeIDs ? idHandler.getLastObjectID() : CDOID.NULL;
+ CDOID nextLocalObjectID = storeIDs ? idHandler.getNextLocalObjectID() : CDOID.NULL;
+
+ int branchID = DBUtil.selectMaximumInt(connection, CDODBSchema.BRANCHES_ID);
+ setLastBranchID(branchID > 0 ? branchID : 0);
+
+ int localBranchID = DBUtil.selectMinimumInt(connection, CDODBSchema.BRANCHES_ID);
+ setLastLocalBranchID(localBranchID < 0 ? localBranchID : 0);
+
+ long lastCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP);
+ setLastCommitTime(lastCommitTime);
+
+ long lastNonLocalCommitTime = DBUtil.selectMaximumLong(connection, CDODBSchema.COMMIT_INFOS_TIMESTAMP,
+ CDOBranch.MAIN_BRANCH_ID + "<=" + CDODBSchema.COMMIT_INFOS_BRANCH);
+ setLastNonLocalCommitTime(lastNonLocalCommitTime);
+
+ if (storeIDs)
+ {
+ OM.LOG
+ .info(MessageFormat.format(
+ Messages.getString("DBStore.10"), name, lastObjectID, nextLocalObjectID, getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); //$NON-NLS-1$
+ }
+ else
+ {
+ OM.LOG
+ .info(MessageFormat.format(
+ Messages.getString("DBStore.10b"), name, getLastBranchID(), getLastCommitTime(), getLastNonLocalCommitTime())); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException e)
+ {
+ OM.LOG.error(MessageFormat.format(Messages.getString("DBStore.11"), name), e); //$NON-NLS-1$
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(connection);
+ }
+ }
+
+ protected IDBSchema createSchema()
+ {
+ String name = getRepository().getName();
+ return new DBSchema(name);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java
new file mode 100644
index 0000000000..42aca92d88
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java
@@ -0,0 +1,1393 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 259402
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Andre Dietisheim - bug 256649
+ * Caspar De Groot - maintenance
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+import org.eclipse.emf.cdo.spi.server.StoreAccessor;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.io.IOUtil;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimerTask;
+
+/**
+ * @author Eike Stepper
+ */
+public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking2
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class);
+
+ private Connection connection;
+
+ private ConnectionKeepAliveTask connectionKeepAliveTask;
+
+ private IPreparedStatementCache statementCache;
+
+ private Set<CDOID> newObjects = new HashSet<CDOID>();
+
+ private CDOID maxID = CDOID.NULL;
+
+ public DBStoreAccessor(DBStore store, ISession session) throws DBException
+ {
+ super(store, session);
+ }
+
+ public DBStoreAccessor(DBStore store, ITransaction transaction) throws DBException
+ {
+ super(store, transaction);
+ }
+
+ @Override
+ public DBStore getStore()
+ {
+ return (DBStore)super.getStore();
+ }
+
+ public IPreparedStatementCache getStatementCache()
+ {
+ return statementCache;
+ }
+
+ public DBStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature)
+ {
+ return new DBStoreChunkReader(this, revision, feature);
+ }
+
+ /**
+ * Returns an iterator that iterates over all objects in the store and makes their CDOIDs available for processing.
+ * This method is supposed to be called very infrequently, for example during the recovery from a crash.
+ *
+ * @since 2.0
+ * @deprecated Not used by the framework anymore.
+ */
+ @Deprecated
+ public CloseableIterator<CDOID> readObjectIDs()
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Selecting object IDs"); //$NON-NLS-1$
+ }
+
+ return getStore().getMappingStrategy().readObjectIDs(this);
+ }
+
+ public CDOClassifierRef readObjectType(CDOID id)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Selecting object type: {0}", id); //$NON-NLS-1$
+ }
+
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ return mappingStrategy.readObjectType(this, id);
+ }
+
+ protected EClass getObjectType(CDOID id)
+ {
+ IRepository repository = getStore().getRepository();
+ if (repository.getRootResourceID().equals(id))
+ {
+ return EresourcePackage.Literals.CDO_RESOURCE;
+ }
+
+ EClass result = repository.getRevisionManager().getObjectType(id);
+ if (result != null)
+ {
+ return result;
+ }
+
+ CDOClassifierRef type = readObjectType(id);
+ if (type != null)
+ {
+ CDOPackageRegistry packageRegistry = repository.getPackageRegistry();
+ return (EClass)type.resolve(packageRegistry);
+ }
+
+ return null;
+ }
+
+ public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
+ CDORevisionCacheAdder cache)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Selecting revision {0} from {1}", id, branchPoint); //$NON-NLS-1$
+ }
+
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+
+ EClass eClass = getObjectType(id);
+ if (eClass != null)
+ {
+ InternalCDORevision revision = getStore().createRevision(eClass, id);
+ revision.setBranchPoint(branchPoint);
+
+ IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
+ if (mapping.readRevision(this, revision, listChunk))
+ {
+ int version = revision.getVersion();
+ if (version < CDOBranchVersion.FIRST_VERSION - 1)
+ {
+ return new DetachedCDORevision(eClass, id, revision.getBranch(), -version, revision.getTimeStamp(),
+ revision.getRevised());
+ }
+
+ return revision;
+ }
+ }
+
+ // Reading failed - revision does not exist.
+ return null;
+ }
+
+ public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
+ CDORevisionCacheAdder cache)
+ {
+ DBStore store = getStore();
+ EClass eClass = getObjectType(id);
+
+ IMappingStrategy mappingStrategy = store.getMappingStrategy();
+ IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
+
+ InternalCDORevision revision = store.createRevision(eClass, id);
+ revision.setVersion(branchVersion.getVersion());
+ revision.setBranchPoint(branchVersion.getBranch().getHead());
+
+ boolean success = false;
+
+ if (mappingStrategy.hasAuditSupport())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Selecting revision {0} from {1}", id, branchVersion); //$NON-NLS-1$
+ }
+
+ // if audit support is present, just use the audit method
+ success = ((IClassMappingAuditSupport)mapping).readRevisionByVersion(this, revision, listChunk);
+ if (success && revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1)
+ {
+ // it is detached revision
+ revision = new DetachedCDORevision(eClass, id, revision.getBranch(), -revision.getVersion(),
+ revision.getTimeStamp(), revision.getRevised());
+
+ }
+ }
+ else
+ {
+ // if audit support is not present, we still have to provide a method
+ // to readRevisionByVersion because TransactionCommitContext.computeDirtyObject
+ // needs to lookup the base revision for a change. Hence we emulate this
+ // behavior by getting the current revision and asserting that the version
+ // has not changed. This is valid because if the version has changed,
+ // we are in trouble because of a conflict anyways.
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Selecting current base revision: {0}", id); //$NON-NLS-1$
+ }
+
+ success = mapping.readRevision(this, revision, listChunk);
+
+ if (success && revision.getVersion() != branchVersion.getVersion())
+ {
+ throw new IllegalStateException("Can only retrieve current version " + revision.getVersion() + " for " + id //$NON-NLS-1$ //$NON-NLS-2$
+ + " - version requested was " + branchVersion); //$NON-NLS-1$
+ }
+ }
+
+ return success ? revision : null;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void queryResources(QueryResourcesContext context)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ mappingStrategy.queryResources(this, context);
+ }
+
+ public void queryXRefs(QueryXRefsContext context)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ mappingStrategy.queryXRefs(this, context);
+ }
+
+ public IQueryHandler getQueryHandler(CDOQueryInfo info)
+ {
+ String queryLanguage = info.getQueryLanguage();
+ if (StringUtil.equalsUpperOrLowerCase(queryLanguage, SQLQueryHandler.QUERY_LANGUAGE))
+ {
+ return new SQLQueryHandler(this);
+ }
+
+ return null;
+ }
+
+ public void queryLobs(List<byte[]> ids)
+ {
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_QUERY_LOBS, ReuseProbability.MEDIUM);
+
+ for (Iterator<byte[]> it = ids.iterator(); it.hasNext();)
+ {
+ byte[] id = it.next();
+ stmt.setString(1, HexUtil.bytesToHex(id));
+
+ try
+ {
+ resultSet = stmt.executeQuery();
+ if (!resultSet.next())
+ {
+ it.remove();
+ }
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void loadLob(byte[] id, OutputStream out) throws IOException
+ {
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_LOB, ReuseProbability.MEDIUM);
+ stmt.setString(1, HexUtil.bytesToHex(id));
+
+ try
+ {
+ resultSet = stmt.executeQuery();
+ resultSet.next();
+
+ long size = resultSet.getLong(1);
+ Blob blob = resultSet.getBlob(2);
+ if (resultSet.wasNull())
+ {
+ Clob clob = resultSet.getClob(3);
+ Reader in = clob.getCharacterStream();
+ IOUtil.copyCharacter(in, new OutputStreamWriter(out), size);
+ }
+ else
+ {
+ InputStream in = blob.getBinaryStream();
+ IOUtil.copyBinary(in, out, size);
+ }
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
+ {
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_HANDLE_LOBS, ReuseProbability.LOW);
+
+ try
+ {
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ byte[] id = HexUtil.hexToBytes(resultSet.getString(1));
+ long size = resultSet.getLong(2);
+ Blob blob = resultSet.getBlob(3);
+ if (resultSet.wasNull())
+ {
+ Clob clob = resultSet.getClob(4);
+ Reader in = clob.getCharacterStream();
+ Writer out = handler.handleClob(id, size);
+ if (out != null)
+ {
+ try
+ {
+ IOUtil.copyCharacter(in, out, size);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+ }
+ else
+ {
+ InputStream in = blob.getBinaryStream();
+ OutputStream out = handler.handleBlob(id, size);
+ if (out != null)
+ {
+ try
+ {
+ IOUtil.copyBinary(in, out, size);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor)
+ {
+ super.applyIDMappings(context, monitor);
+
+ // Remember maxID because it may have to be adjusted if the repository is BACKUP or CLONE. See bug 325097.
+ boolean adjustMaxID = !context.getBranchPoint().getBranch().isLocal()
+ && getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE;
+
+ IIDHandler idHandler = getStore().getIDHandler();
+
+ // Remember CDOIDs of new objects. They are cleared after writeRevisions()
+ for (InternalCDORevision revision : context.getNewObjects())
+ {
+ CDOID id = revision.getID();
+ newObjects.add(id);
+
+ if (adjustMaxID && idHandler.compare(id, maxID) > 0)
+ {
+ maxID = id;
+ }
+ }
+ }
+
+ @Override
+ protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
+ String comment, OMMonitor monitor)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_COMMIT_INFO, ReuseProbability.HIGH);
+ stmt.setLong(1, timeStamp);
+ stmt.setLong(2, previousTimeStamp);
+ stmt.setInt(3, branch.getID());
+ stmt.setString(4, userID);
+ stmt.setString(5, comment);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
+ OMMonitor monitor)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+
+ if (!mappingStrategy.hasDeltaSupport())
+ {
+ throw new UnsupportedOperationException("Mapping strategy does not support revision deltas"); //$NON-NLS-1$
+ }
+
+ monitor.begin(revisionDeltas.length);
+ try
+ {
+ for (InternalCDORevisionDelta delta : revisionDeltas)
+ {
+ writeRevisionDelta(delta, created, monitor.fork());
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ protected void writeRevisionDelta(InternalCDORevisionDelta delta, long created, OMMonitor monitor)
+ {
+ CDOID id = delta.getID();
+ EClass eClass = getObjectType(id);
+ IClassMappingDeltaSupport mapping = (IClassMappingDeltaSupport)getStore().getMappingStrategy().getClassMapping(
+ eClass);
+ mapping.writeRevisionDelta(this, delta, created, monitor);
+ }
+
+ @Override
+ protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(revisions.length);
+ for (InternalCDORevision revision : revisions)
+ {
+ writeRevision(revision, newObjects.contains(revision.getID()), true, monitor.fork());
+ }
+ }
+ finally
+ {
+ newObjects.clear();
+ monitor.done();
+ }
+ }
+
+ protected void writeRevision(InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing revision: {0}", revision); //$NON-NLS-1$
+ }
+
+ EClass eClass = revision.getEClass();
+
+ IClassMapping mapping = getStore().getMappingStrategy().getClassMapping(eClass);
+ mapping.writeRevision(this, revision, mapType, revise, monitor);
+ }
+
+ /*
+ * XXX Eike: change API from CDOID[] to CDOIDAndVersion[]
+ */
+ @Override
+ protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ monitor.begin(detachedObjects.length);
+
+ try
+ {
+ InternalCDORevisionManager revisionManager = getStore().getRepository().getRevisionManager();
+ for (CDOID id : detachedObjects)
+ {
+ // TODO when CDOIDAndVersion is available:
+ // CDOID id = idAndVersion.getID(); //
+ // int version = idAndVersion.getVersion(); //
+
+ // but for now:
+
+ InternalCDORevision revision = revisionManager.getRevision(id, branch.getHead(), CDORevision.UNCHUNKED,
+ CDORevision.DEPTH_NONE, true);
+ int version = ObjectUtil.equals(branch, revision.getBranch()) ? revision.getVersion()
+ : CDOBranchVersion.FIRST_VERSION;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Detaching object: {0}", id); //$NON-NLS-1$
+ }
+
+ EClass eClass = getObjectType(id);
+ IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
+ mapping.detachObject(this, id, version, branch, timeStamp, monitor.fork());
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public Connection getConnection()
+ {
+ return connection;
+ }
+
+ @Override
+ protected CDOID getNextCDOID(CDORevision revision)
+ {
+ return getStore().getIDHandler().getNextCDOID(revision);
+ }
+
+ @Override
+ protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_BLOB, ReuseProbability.MEDIUM);
+ stmt.setString(1, HexUtil.bytesToHex(id));
+ stmt.setLong(2, size);
+ stmt.setBinaryStream(3, inputStream, (int)size);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void writeClob(byte[] id, long size, Reader reader) throws IOException
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_CLOB, ReuseProbability.MEDIUM);
+ stmt.setString(1, HexUtil.bytesToHex(id));
+ stmt.setLong(2, size);
+ stmt.setCharacterStream(3, reader, (int)size);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected final void doCommit(OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("--- DB COMMIT ---"); //$NON-NLS-1$
+ }
+
+ Async async = null;
+ monitor.begin();
+
+ try
+ {
+ try
+ {
+ async = monitor.forkAsync();
+ getConnection().commit();
+
+ if (maxID != CDOID.NULL)
+ {
+ // See bug 325097
+ getStore().getIDHandler().adjustLastObjectID(maxID);
+ maxID = CDOID.NULL;
+ }
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ @Override
+ protected final void doRollback(IStoreAccessor.CommitContext commitContext)
+ {
+ getStore().getMetaDataManager().clearMetaIDMappings();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("--- DB ROLLBACK ---"); //$NON-NLS-1$
+ }
+
+ try
+ {
+ getConnection().rollback();
+
+ // Bugzilla 298632: Must rollback DBSchema to its prior state and drop the tables
+ getStore().getMappingStrategy().removeMapping(getConnection(), commitContext.getNewPackageUnits());
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ DBStore store = getStore();
+ connection = store.getConnection();
+ connectionKeepAliveTask = new ConnectionKeepAliveTask();
+
+ long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD;
+ Map<String, String> storeProps = store.getProperties();
+ if (storeProps != null)
+ {
+ String value = storeProps.get(IDBStore.Props.CONNECTION_KEEPALIVE_PERIOD);
+ if (value != null)
+ {
+ keepAlivePeriod = Long.parseLong(value) * 60L * 1000L;
+ }
+ }
+
+ store.getConnectionKeepAliveTimer().schedule(connectionKeepAliveTask, keepAlivePeriod, keepAlivePeriod);
+
+ // TODO - make this configurable?
+ statementCache = CDODBUtil.createStatementCache();
+ statementCache.setConnection(connection);
+ LifecycleUtil.activate(statementCache);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(statementCache);
+ connectionKeepAliveTask.cancel();
+ DBUtil.close(connection);
+ connection = null;
+ }
+
+ @Override
+ protected void doPassivate() throws Exception
+ {
+ // this is called when the accessor is put back into the pool
+ // we want to make sure that no DB lock is held (see Bug 276926)
+ connection.rollback();
+ }
+
+ @Override
+ protected void doUnpassivate() throws Exception
+ {
+ // do nothing
+ }
+
+ public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit)
+ {
+ return getStore().getMetaDataManager().loadPackageUnit(getConnection(), packageUnit);
+ }
+
+ public Collection<InternalCDOPackageUnit> readPackageUnits()
+ {
+ return getStore().getMetaDataManager().readPackageUnits(getConnection());
+ }
+
+ public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ monitor.begin(2);
+
+ try
+ {
+ DBStore store = getStore();
+ Connection connection = getConnection();
+
+ IMetaDataManager metaDataManager = store.getMetaDataManager();
+ metaDataManager.writePackageUnits(connection, packageUnits, monitor.fork());
+
+ IMappingStrategy mappingStrategy = store.getMappingStrategy();
+ mappingStrategy.createMapping(connection, packageUnits, monitor.fork());
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
+ {
+ checkBranchingSupport();
+ if (branchID == NEW_BRANCH)
+ {
+ branchID = getStore().getNextBranchID();
+ }
+ else if (branchID == NEW_LOCAL_BRANCH)
+ {
+ branchID = getStore().getNextLocalBranchID();
+ }
+
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_BRANCH, ReuseProbability.LOW);
+ stmt.setInt(1, branchID);
+ stmt.setString(2, branchInfo.getName());
+ stmt.setInt(3, branchInfo.getBaseBranchID());
+ stmt.setLong(4, branchInfo.getBaseTimeStamp());
+
+ DBUtil.update(stmt, true);
+ getConnection().commit();
+ return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp());
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public BranchInfo loadBranch(int branchID)
+ {
+ checkBranchingSupport();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCH, ReuseProbability.HIGH);
+ stmt.setInt(1, branchID);
+
+ resultSet = stmt.executeQuery();
+ if (!resultSet.next())
+ {
+ throw new DBException("Branch with ID " + branchID + " does not exist");
+ }
+
+ String name = resultSet.getString(1);
+ int baseBranchID = resultSet.getInt(2);
+ long baseTimeStamp = resultSet.getLong(3);
+ return new BranchInfo(name, baseBranchID, baseTimeStamp);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public SubBranchInfo[] loadSubBranches(int baseID)
+ {
+ checkBranchingSupport();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_SUB_BRANCHES, ReuseProbability.HIGH);
+ stmt.setInt(1, baseID);
+
+ resultSet = stmt.executeQuery();
+ List<SubBranchInfo> result = new ArrayList<SubBranchInfo>();
+ while (resultSet.next())
+ {
+ int id = resultSet.getInt(1);
+ String name = resultSet.getString(2);
+ long baseTimeStamp = resultSet.getLong(3);
+ result.add(new SubBranchInfo(id, name, baseTimeStamp));
+ }
+
+ return result.toArray(new SubBranchInfo[result.size()]);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void checkBranchingSupport()
+ {
+ if (!getStore().getMappingStrategy().hasBranchingSupport())
+ {
+ throw new UnsupportedOperationException("Mapping strategy does not support branching"); //$NON-NLS-1$
+ }
+ }
+
+ public int loadBranches(int startID, int endID, CDOBranchHandler handler)
+ {
+ int count = 0;
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ InternalRepository repository = getSession().getManager().getRepository();
+ InternalCDOBranchManager branchManager = repository.getBranchManager();
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCHES, ReuseProbability.HIGH);
+ stmt.setInt(1, startID);
+ stmt.setInt(2, endID > 0 ? endID : Integer.MAX_VALUE);
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ int branchID = resultSet.getInt(1);
+ String name = resultSet.getString(2);
+ int baseBranchID = resultSet.getInt(3);
+ long baseTimeStamp = resultSet.getLong(4);
+
+ InternalCDOBranch branch = branchManager.getBranch(branchID, new BranchInfo(name, baseBranchID, baseTimeStamp));
+ handler.handleBranch(branch);
+ ++count;
+ }
+
+ return count;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_PREVIOUS_TIMESTAMP);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_USER);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_COMMENT);
+ if (branch == null)
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_BRANCH);
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS);
+ boolean where = false;
+
+ if (branch != null)
+ {
+ builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
+ builder.append(CDODBSchema.COMMIT_INFOS_BRANCH);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(branch.getID());
+ where = true;
+ }
+
+ if (startTime != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
+ builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
+ builder.append(">="); //$NON-NLS-1$
+ builder.append(startTime);
+ where = true;
+ }
+
+ if (endTime != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
+ builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
+ builder.append("<="); //$NON-NLS-1$
+ builder.append(endTime);
+ where = true;
+ }
+
+ builder.append(" ORDER BY "); //$NON-NLS-1$
+ builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
+ String sql = builder.toString();
+
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ InternalRepository repository = getStore().getRepository();
+ InternalCDOBranchManager branchManager = repository.getBranchManager();
+ InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager();
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM);
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ long timeStamp = resultSet.getLong(1);
+ long previousTimeStamp = resultSet.getLong(2);
+ String userID = resultSet.getString(3);
+ String comment = resultSet.getString(4);
+ CDOBranch infoBranch = branch;
+ if (infoBranch == null)
+ {
+ int id = resultSet.getInt(5);
+ infoBranch = branchManager.getBranch(id);
+ }
+
+ CDOCommitInfo commitInfo = commitInfoManager.createCommitInfo(infoBranch, timeStamp, previousTimeStamp, userID,
+ comment, null);
+ handler.handleCommitInfo(commitInfo);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ return mappingStrategy.readChangeSet(this, monitor, segments);
+ }
+
+ public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ mappingStrategy.handleRevisions(this, eClass, branch, timeStamp, exactTime, new DBRevisionHandler(handler));
+ }
+
+ public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ DBStore store = getStore();
+ if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ out.writeCDOID(store.getIDHandler().getLastObjectID()); // See bug 325097
+ }
+
+ String where = " WHERE " + CDODBSchema.BRANCHES_ID + " BETWEEN " + fromBranchID + " AND " + toBranchID;
+ DBUtil.serializeTable(out, connection, CDODBSchema.BRANCHES, null, where);
+
+ where = " WHERE " + CDODBSchema.COMMIT_INFOS_TIMESTAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
+ DBUtil.serializeTable(out, connection, CDODBSchema.COMMIT_INFOS, null, where);
+
+ IIDHandler idHandler = store.getIDHandler();
+ idHandler.rawExport(connection, out, fromCommitTime, toCommitTime);
+
+ IMetaDataManager metaDataManager = store.getMetaDataManager();
+ metaDataManager.rawExport(connection, out, fromCommitTime, toCommitTime);
+
+ IMappingStrategy mappingStrategy = store.getMappingStrategy();
+ mappingStrategy.rawExport(this, out, fromBranchID, toBranchID, fromCommitTime, toCommitTime);
+ }
+
+ public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ DBStore store = getStore();
+ if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ store.getIDHandler().setLastObjectID(in.readCDOID()); // See bug 325097
+ }
+
+ IMappingStrategy mappingStrategy = store.getMappingStrategy();
+ int size = mappingStrategy.getClassMappings().size();
+ int commitWork = 4;
+ monitor.begin(commitWork + size + commitWork);
+
+ Collection<InternalCDOPackageUnit> packageUnits = new HashSet<InternalCDOPackageUnit>();
+
+ try
+ {
+ DBUtil.deserializeTable(in, connection, CDODBSchema.BRANCHES, monitor.fork());
+ DBUtil.deserializeTable(in, connection, CDODBSchema.COMMIT_INFOS, monitor.fork());
+ store.getIDHandler().rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork());
+ rawImportPackageUnits(in, fromCommitTime, toCommitTime, packageUnits, monitor.fork());
+ mappingStrategy.rawImport(this, in, fromCommitTime, toCommitTime, monitor.fork(size));
+ rawCommit(commitWork, monitor);
+ }
+ catch (RuntimeException ex)
+ {
+ rawRollback(packageUnits);
+ throw ex;
+ }
+ catch (IOException ex)
+ {
+ rawRollback(packageUnits);
+ throw ex;
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void rawRollback(Collection<InternalCDOPackageUnit> packageUnits)
+ {
+ try
+ {
+ connection.rollback();
+ }
+ catch (SQLException ex)
+ {
+ OM.LOG.error(ex);
+ }
+
+ getStore().getMappingStrategy().removeMapping(getConnection(),
+ packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]));
+ }
+
+ protected void rawImportPackageUnits(CDODataInput in, long fromCommitTime, long toCommitTime,
+ Collection<InternalCDOPackageUnit> packageUnits, OMMonitor monitor) throws IOException
+ {
+ monitor.begin(2);
+
+ try
+ {
+ DBStore store = getStore();
+ IMetaDataManager metaDataManager = store.getMetaDataManager();
+
+ packageUnits.addAll(metaDataManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()));
+
+ InternalRepository repository = store.getRepository();
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ packageRegistry.putPackageUnit(packageUnit);
+ }
+
+ IMappingStrategy mappingStrategy = store.getMappingStrategy();
+
+ // Using another connection because CREATE TABLE (which is called in createMapping) on H2 databases does a commit.
+ Connection connection2 = null;
+ try
+ {
+ connection2 = store.getConnection();
+
+ mappingStrategy.createMapping(connection2,
+ packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]), monitor.fork());
+ }
+ finally
+ {
+ DBUtil.close(connection2);
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ writePackageUnits(packageUnits, monitor);
+ }
+
+ public void rawStore(InternalCDORevision revision, OMMonitor monitor)
+ {
+ CDOID id = revision.getID();
+
+ CDOClassifierRef classifierRef = getStore().getMappingStrategy().readObjectType(this, id);
+ boolean isFirstRevision = classifierRef == null;
+
+ if (!isFirstRevision)
+ {
+ EClass eClass = revision.getEClass();
+ boolean namesMatch = classifierRef.getClassifierName().equals(eClass.getName());
+ boolean packagesMatch = classifierRef.getPackageURI().equals(eClass.getEPackage().getNsURI());
+ if (!namesMatch || !packagesMatch)
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ writeRevision(revision, isFirstRevision, false, monitor);
+ getStore().getIDHandler().adjustLastObjectID(id);
+ }
+
+ public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException
+ {
+ writeBlob(id, size, inputStream);
+ }
+
+ public void rawStore(byte[] id, long size, Reader reader) throws IOException
+ {
+ writeClob(id, size, reader);
+ }
+
+ public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
+ OMMonitor monitor)
+ {
+ writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor);
+ }
+
+ @Deprecated
+ public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException();
+
+ // IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
+ // if (eClass == null)
+ // {
+ // eClass = getObjectType(id);
+ // }
+ //
+ // IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
+ // mapping.detachObject(this, id, version, branch, CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
+
+ public void rawCommit(double commitWork, OMMonitor monitor)
+ {
+ monitor.begin();
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ async.stop();
+ monitor.done();
+ }
+ }
+
+ public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ return createLockArea(null, userID, branchPoint, readOnly, locks);
+ }
+
+ public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
+ Map<CDOID, LockGrade> locks)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ return manager.createLockArea(this, durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+
+ public void updateLockArea(LockArea area)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.updateLockArea(this, area);
+ }
+
+ public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ return manager.getLockArea(this, durableLockingID);
+ }
+
+ public void getLockAreas(String userIDPrefix, Handler handler)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.getLockAreas(this, userIDPrefix, handler);
+ }
+
+ public void deleteLockArea(String durableLockingID)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.deleteLockArea(this, durableLockingID);
+ }
+
+ public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.lock(this, durableLockingID, type, objectsToLock);
+ }
+
+ public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.unlock(this, durableLockingID, type, objectsToUnlock);
+ }
+
+ public void unlock(String durableLockingID)
+ {
+ DurableLockingManager manager = getStore().getDurableLockingManager();
+ manager.unlock(this, durableLockingID);
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class ConnectionKeepAliveTask extends TimerTask
+ {
+ public static final long EXECUTION_PERIOD = 1000 * 60 * 60 * 4; // 4 hours
+
+ @Override
+ public void run()
+ {
+ Statement stmt = null;
+
+ try
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("DB connection keep-alive task activated"); //$NON-NLS-1$
+ }
+
+ stmt = connection.createStatement();
+ stmt.executeQuery("SELECT 1 FROM " + CDODBSchema.PROPERTIES); //$NON-NLS-1$
+ }
+ catch (java.sql.SQLException ex)
+ {
+ OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$
+
+ // Assume the connection has failed.
+ try
+ {
+ LifecycleUtil.deactivate(DBStoreAccessor.this);
+ LifecycleUtil.activate(DBStoreAccessor.this);
+ }
+ catch (Exception ex2)
+ {
+ OM.LOG.error("DB connection reconnect failed", ex2); //$NON-NLS-1$
+ }
+ }
+ catch (Exception ex) // Important: Do not throw any unchecked exceptions to the TimerThread!!!
+ {
+ OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$
+ }
+ finally
+ {
+ DBUtil.close(stmt);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java
new file mode 100644
index 0000000000..59287fa3ee
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreChunkReader.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.spi.server.StoreChunkReader;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ */
+public class DBStoreChunkReader extends StoreChunkReader implements IDBStoreChunkReader
+{
+ private IListMapping referenceMapping;
+
+ private StringBuilder builder = new StringBuilder();
+
+ public DBStoreChunkReader(DBStoreAccessor accessor, CDORevision revision, EStructuralFeature feature)
+ {
+ super(accessor, revision, feature);
+ IMappingStrategy mappingStrategy = accessor.getStore().getMappingStrategy();
+ IClassMapping mapping = mappingStrategy.getClassMapping(revision.getEClass());
+ referenceMapping = mapping.getListMapping(feature);
+ }
+
+ @Override
+ public DBStoreAccessor getAccessor()
+ {
+ return (DBStoreAccessor)super.getAccessor();
+ }
+
+ @Override
+ public void addSimpleChunk(int index)
+ {
+ super.addSimpleChunk(index);
+ prepareAddition();
+
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append('=');
+ builder.append(index);
+ }
+
+ @Override
+ public void addRangedChunk(int fromIndex, int toIndex)
+ {
+ super.addRangedChunk(fromIndex, toIndex);
+ prepareAddition();
+
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append(" BETWEEN "); //$NON-NLS-1$
+ builder.append(fromIndex);
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(toIndex - 1);
+ }
+
+ public List<Chunk> executeRead()
+ {
+ List<Chunk> chunks = getChunks();
+ if (chunks.size() > 1)
+ {
+ builder.insert(0, '(');
+ builder.append(')');
+ }
+
+ referenceMapping.readChunks(this, chunks, builder.toString());
+ return chunks;
+ }
+
+ private void prepareAddition()
+ {
+ // If not empty, a chunk has been already added, and the next condition needs to be OR-ed
+ if (builder.length() > 0)
+ {
+ builder.append(" OR "); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java
new file mode 100644
index 0000000000..229028902b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreFactory.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Caspar De Groot - maintenance
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStoreFactory;
+import org.eclipse.emf.cdo.server.db.CDODBUtil;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.spi.server.RepositoryConfigurator;
+
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+import javax.sql.DataSource;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Eike Stepper
+ */
+public class DBStoreFactory implements IStoreFactory
+{
+ public DBStoreFactory()
+ {
+ }
+
+ public String getStoreType()
+ {
+ return DBStore.TYPE;
+ }
+
+ public IStore createStore(String repositoryName, Map<String, String> repositoryProperties, Element storeConfig)
+ {
+ IMappingStrategy mappingStrategy = getMappingStrategy(repositoryName, repositoryProperties, storeConfig);
+ IDBAdapter dbAdapter = getDBAdapter(storeConfig);
+ DataSource dataSource = getDataSource(storeConfig);
+ IDBConnectionProvider connectionProvider = DBUtil.createConnectionProvider(dataSource);
+
+ DBStore store = new DBStore();
+ store.setMappingStrategy(mappingStrategy);
+ store.setDBAdapter(dbAdapter);
+ store.setDbConnectionProvider(connectionProvider);
+
+ Map<String, String> storeProperties = RepositoryConfigurator.getProperties(storeConfig, 1);
+ store.setProperties(storeProperties);
+
+ return store;
+ }
+
+ private IMappingStrategy getMappingStrategy(String repositoryName, Map<String, String> repositoryProperties,
+ Element storeConfig)
+ {
+ NodeList mappingStrategyConfigs = storeConfig.getElementsByTagName("mappingStrategy"); //$NON-NLS-1$
+ if (mappingStrategyConfigs.getLength() != 1)
+ {
+ throw new IllegalStateException("Exactly one mapping strategy must be configured for DB store"); //$NON-NLS-1$
+ }
+
+ Element mappingStrategyConfig = (Element)mappingStrategyConfigs.item(0);
+ String mappingStrategyType = mappingStrategyConfig.getAttribute("type"); //$NON-NLS-1$
+ IMappingStrategy mappingStrategy = CDODBUtil.createMappingStrategy(mappingStrategyType);
+ if (mappingStrategy == null)
+ {
+ throw new IllegalArgumentException("Unknown mapping strategy: " + mappingStrategyType); //$NON-NLS-1$
+ }
+
+ Map<String, String> properties = RepositoryConfigurator.getProperties(mappingStrategyConfig, 1);
+ properties.put("repositoryName", repositoryName);
+ properties.putAll(repositoryProperties);
+ mappingStrategy.setProperties(properties);
+
+ return mappingStrategy;
+ }
+
+ private IDBAdapter getDBAdapter(Element storeConfig)
+ {
+ NodeList dbAdapterConfigs = storeConfig.getElementsByTagName("dbAdapter"); //$NON-NLS-1$
+ if (dbAdapterConfigs.getLength() != 1)
+ {
+ throw new IllegalStateException("Exactly one dbAdapter must be configured for DB store"); //$NON-NLS-1$
+ }
+
+ Element dbAdapterConfig = (Element)dbAdapterConfigs.item(0);
+ String dbAdapterName = dbAdapterConfig.getAttribute("name"); //$NON-NLS-1$
+ IDBAdapter dbAdapter = DBUtil.getDBAdapter(dbAdapterName);
+ if (dbAdapter == null)
+ {
+ throw new IllegalArgumentException("Unknown DB adapter: " + dbAdapterName); //$NON-NLS-1$
+ }
+
+ return dbAdapter;
+ }
+
+ private DataSource getDataSource(Element storeConfig)
+ {
+ NodeList dataSourceConfigs = storeConfig.getElementsByTagName("dataSource"); //$NON-NLS-1$
+ if (dataSourceConfigs.getLength() != 1)
+ {
+ throw new IllegalStateException("Exactly one dataSource must be configured for DB store"); //$NON-NLS-1$
+ }
+
+ Properties properties = new Properties();
+ Element dataSourceConfig = (Element)dataSourceConfigs.item(0);
+ NamedNodeMap attributes = dataSourceConfig.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ Attr attribute = (Attr)attributes.item(i);
+ properties.put(attribute.getName(), attribute.getValue());
+ }
+
+ return DBUtil.createDataSource(properties);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java
new file mode 100644
index 0000000000..7fbd726665
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java
@@ -0,0 +1,687 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaAlreadyExistsException;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
+import org.eclipse.emf.cdo.spi.server.InternalLockManager;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author Eike Stepper
+ */
+public class DurableLockingManager extends Lifecycle
+{
+ private DBStore store;
+
+ private InternalCDOBranchManager branchManager;
+
+ private IIDHandler idHandler;
+
+ private IDBTable lockAreas;
+
+ private IDBField lockAreasID;
+
+ private IDBField lockAreasUser;
+
+ private IDBField lockAreasBranch;
+
+ private IDBField lockAreasTime;
+
+ private IDBField lockAreasReadOnly;
+
+ private IDBTable locks;
+
+ private IDBField locksArea;
+
+ private IDBField locksObject;
+
+ private IDBField locksGrade;
+
+ private String sqlInsertLockArea;
+
+ private String sqlSelectLockArea;
+
+ private String sqlSelectAllLockAreas;
+
+ private String sqlSelectLockAreas;
+
+ private String sqlDeleteLockArea;
+
+ private String sqlSelectLocks;
+
+ private String sqlSelectLock;
+
+ private String sqlInsertLock;
+
+ private String sqlUpdateLock;
+
+ private String sqlDeleteLock;
+
+ private String sqlDeleteLocks;
+
+ public DurableLockingManager(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public synchronized LockArea createLockArea(DBStoreAccessor accessor, String durableLockingID, String userID,
+ CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks)
+ {
+ try
+ {
+ if (durableLockingID == null)
+ {
+ durableLockingID = getNextDurableLockingID(accessor);
+ }
+ else
+ {
+ // If the caller is specifying the ID, make sure there is no area with this ID yet
+ //
+ try
+ {
+ getLockArea(accessor, durableLockingID);
+ throw new LockAreaAlreadyExistsException(durableLockingID);
+ }
+ catch (LockAreaNotFoundException good)
+ {
+ }
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertLockArea, ReuseProbability.LOW);
+ stmt.setString(1, durableLockingID);
+ stmt.setString(2, userID);
+ stmt.setInt(3, branchPoint.getBranch().getID());
+ stmt.setLong(4, branchPoint.getTimeStamp());
+ stmt.setBoolean(5, readOnly);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (!locks.isEmpty())
+ {
+ insertLocks(accessor, durableLockingID, locks);
+ }
+
+ accessor.getConnection().commit();
+
+ return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ private void insertLocks(DBStoreAccessor accessor, String durableLockingID, Map<CDOID, LockGrade> locks)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertLock, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+
+ for (Entry<CDOID, LockGrade> entry : locks.entrySet())
+ {
+ CDOID id = entry.getKey();
+ int grade = entry.getValue().getValue();
+
+ idHandler.setCDOID(stmt, 2, id);
+ stmt.setInt(3, grade);
+
+ DBUtil.update(stmt, true);
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public LockArea getLockArea(DBStoreAccessor accessor, String durableLockingID) throws LockAreaNotFoundException
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLockArea, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+ resultSet = stmt.executeQuery();
+
+ if (!resultSet.next())
+ {
+ throw new LockAreaNotFoundException(durableLockingID);
+ }
+
+ String userID = resultSet.getString(1);
+ int branchID = resultSet.getInt(2);
+ long timeStamp = resultSet.getLong(3);
+ boolean readOnly = resultSet.getBoolean(4);
+
+ return makeLockArea(accessor, durableLockingID, userID, branchID, timeStamp, readOnly);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void getLockAreas(DBStoreAccessor accessor, String userIDPrefix, Handler handler)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ if (userIDPrefix.length() == 0)
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectAllLockAreas, ReuseProbability.MEDIUM);
+ }
+ else
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLockAreas, ReuseProbability.MEDIUM);
+ stmt.setString(1, userIDPrefix + "%");
+ }
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ String durableLockingID = resultSet.getString(1);
+ String userID = resultSet.getString(2);
+ int branchID = resultSet.getInt(3);
+ long timeStamp = resultSet.getLong(4);
+ boolean readOnly = resultSet.getBoolean(5);
+
+ LockArea area = makeLockArea(accessor, durableLockingID, userID, branchID, timeStamp, readOnly);
+ if (!handler.handleLockArea(area))
+ {
+ break;
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void deleteLockArea(DBStoreAccessor accessor, String durableLockingID)
+ {
+ try
+ {
+ unlockWithoutCommit(accessor, durableLockingID);
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDeleteLockArea, ReuseProbability.LOW);
+ stmt.setString(1, durableLockingID);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ public void updateLockArea(DBStoreAccessor accessor, LockArea area)
+ {
+ try
+ {
+ String areaID = area.getDurableLockingID();
+ unlockWithoutCommit(accessor, areaID);
+ insertLocks(accessor, areaID, area.getLocks());
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ public void lock(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> objectsToLock)
+ {
+ changeLocks(accessor, durableLockingID, type, objectsToLock, true);
+ }
+
+ public void unlock(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> objectsToUnlock)
+ {
+ changeLocks(accessor, durableLockingID, type, objectsToUnlock, false);
+ }
+
+ public void unlock(DBStoreAccessor accessor, String durableLockingID)
+ {
+ try
+ {
+ unlockWithoutCommit(accessor, durableLockingID);
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ }
+
+ private void unlockWithoutCommit(DBStoreAccessor accessor, String durableLockingID)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlDeleteLocks, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ branchManager = store.getRepository().getBranchManager();
+ idHandler = store.getIDHandler();
+
+ IDBSchema schema = store.getDBSchema();
+
+ // Lock areas
+ lockAreas = schema.addTable("cdo_lock_areas");
+ lockAreasID = lockAreas.addField("id", DBType.VARCHAR);
+ lockAreasUser = lockAreas.addField("user_id", DBType.VARCHAR);
+ lockAreasBranch = lockAreas.addField("view_branch", DBType.INTEGER);
+ lockAreasTime = lockAreas.addField("view_time", DBType.BIGINT);
+ lockAreasReadOnly = lockAreas.addField("read_only", DBType.BOOLEAN);
+
+ lockAreas.addIndex(IDBIndex.Type.PRIMARY_KEY, lockAreasID);
+ lockAreas.addIndex(IDBIndex.Type.NON_UNIQUE, lockAreasUser);
+
+ // Locks
+ locks = schema.addTable("cdo_locks");
+ locksArea = locks.addField("area_id", DBType.VARCHAR);
+ locksObject = locks.addField("object_id", idHandler.getDBType());
+ locksGrade = locks.addField("lock_grade", DBType.INTEGER);
+
+ locks.addIndex(IDBIndex.Type.PRIMARY_KEY, locksArea, locksObject);
+ locks.addIndex(IDBIndex.Type.NON_UNIQUE, locksArea);
+
+ IDBStoreAccessor writer = store.getWriter(null);
+ Connection connection = writer.getConnection();
+ Statement statement = null;
+
+ try
+ {
+ statement = connection.createStatement();
+ store.getDBAdapter().createTable(lockAreas, statement);
+ store.getDBAdapter().createTable(locks, statement);
+ connection.commit();
+ }
+ catch (SQLException ex)
+ {
+ connection.rollback();
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(statement);
+ writer.release();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(") VALUES (?, ?, ?, ?, ?)"); //$NON-NLS-1$
+ sqlInsertLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasBranch);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasTime);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(lockAreasReadOnly);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ sqlSelectAllLockAreas = builder.toString();
+
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasUser);
+ builder.append(" LIKE ?"); //$NON-NLS-1$
+ sqlSelectLockAreas = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(lockAreas);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(lockAreasID);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLockArea = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLocks = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append(","); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append(") VALUES (?, ?, ?)"); //$NON-NLS-1$
+ sqlInsertLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(locksGrade);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlUpdateLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(locksObject);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLock = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(locks);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(locksArea);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteLocks = builder.toString();
+ }
+
+ private String getNextDurableLockingID(DBStoreAccessor accessor)
+ {
+ for (;;)
+ {
+ String durableLockingID = CDOLockUtil.createDurableLockingID();
+
+ try
+ {
+ getLockArea(accessor, durableLockingID); // Check uniqueness
+ // Not unique; try once more...
+ }
+ catch (LockAreaNotFoundException ex)
+ {
+ return durableLockingID;
+ }
+ }
+ }
+
+ private LockArea makeLockArea(DBStoreAccessor accessor, String durableLockingID, String userID, int branchID,
+ long timeStamp, boolean readOnly)
+ {
+ CDOBranchPoint branchPoint = branchManager.getBranch(branchID).getPoint(timeStamp);
+ Map<CDOID, LockGrade> lockMap = getLockMap(accessor, durableLockingID);
+ return CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, lockMap);
+ }
+
+ private Map<CDOID, LockGrade> getLockMap(DBStoreAccessor accessor, String durableLockingID)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectLocks, ReuseProbability.MEDIUM);
+ stmt.setString(1, durableLockingID);
+ resultSet = stmt.executeQuery();
+
+ Map<CDOID, LockGrade> lockMap = new HashMap<CDOID, LockGrade>();
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ int grade = resultSet.getInt(2);
+
+ lockMap.put(id, LockGrade.get(grade));
+ }
+
+ return lockMap;
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void changeLocks(DBStoreAccessor accessor, String durableLockingID, LockType type,
+ Collection<? extends Object> keys, boolean on)
+ {
+ if (keys.isEmpty())
+ {
+ return;
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmtSelect = null;
+ PreparedStatement stmtInsertOrDelete = null;
+ PreparedStatement stmtUpdate = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmtSelect = statementCache.getPreparedStatement(sqlSelectLock, ReuseProbability.MEDIUM);
+ stmtSelect.setString(1, durableLockingID);
+
+ String sql = on ? sqlInsertLock : sqlDeleteLock;
+ stmtInsertOrDelete = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM);
+ stmtInsertOrDelete.setString(1, durableLockingID);
+
+ stmtUpdate = statementCache.getPreparedStatement(sqlUpdateLock, ReuseProbability.MEDIUM);
+ stmtUpdate.setString(2, durableLockingID);
+
+ InternalLockManager lockManager = accessor.getStore().getRepository().getLockManager();
+ for (Object key : keys)
+ {
+ CDOID id = lockManager.getLockKeyID(key);
+ idHandler.setCDOID(stmtSelect, 2, id);
+ resultSet = stmtSelect.executeQuery();
+
+ LockGrade oldGrade = LockGrade.NONE;
+ if (resultSet.next())
+ {
+ oldGrade = LockGrade.get(resultSet.getInt(1));
+ }
+
+ LockGrade newGrade = oldGrade.getUpdated(type, on);
+ if (on && oldGrade == LockGrade.NONE)
+ {
+ idHandler.setCDOID(stmtInsertOrDelete, 2, id);
+ stmtInsertOrDelete.setInt(3, newGrade.getValue());
+ DBUtil.update(stmtInsertOrDelete, true);
+ }
+ else if (!on && newGrade == LockGrade.NONE)
+ {
+ idHandler.setCDOID(stmtInsertOrDelete, 2, id);
+ DBUtil.update(stmtInsertOrDelete, true);
+ }
+ else
+ {
+ stmtUpdate.setInt(1, newGrade.getValue());
+ idHandler.setCDOID(stmtUpdate, 3, id);
+ DBUtil.update(stmtUpdate, true);
+ }
+ }
+
+ accessor.getConnection().commit();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmtUpdate);
+ statementCache.releasePreparedStatement(stmtInsertOrDelete);
+ statementCache.releasePreparedStatement(stmtSelect);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java
new file mode 100644
index 0000000000..34b5f895d3
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ExternalReferenceManager.java
@@ -0,0 +1,302 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ * Stefan Winkler - bug 249610: [DB] Support external references (Implementation)
+ * Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.id.CDOIDExternal;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author Stefan Winkler
+ */
+public class ExternalReferenceManager extends Lifecycle
+{
+ private static final int NULL = 0;
+
+ private IDBTable table;
+
+ private IDBField idField;
+
+ private IDBField uriField;
+
+ private IDBField timestampField;
+
+ private final IIDHandler idHandler;
+
+ private AtomicLong lastMappedID = new AtomicLong(NULL);
+
+ @ExcludeFromDump
+ private transient String sqlSelectByLongID;
+
+ @ExcludeFromDump
+ private transient String sqlSelectByURI;
+
+ @ExcludeFromDump
+ private transient String sqlInsert;
+
+ public ExternalReferenceManager(IIDHandler idHandler)
+ {
+ this.idHandler = idHandler;
+ }
+
+ public IIDHandler getIDHandler()
+ {
+ return idHandler;
+ }
+
+ public long mapExternalReference(CDOIDExternal id, long commitTime)
+ {
+ IDBStoreAccessor accessor = getAccessor();
+ return mapURI(accessor, id.getURI(), commitTime);
+ }
+
+ public CDOIDExternal unmapExternalReference(long mappedId)
+ {
+ IDBStoreAccessor accessor = getAccessor();
+ return CDOIDUtil.createExternal(unmapURI(accessor, mappedId));
+ }
+
+ public long mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ long result = lookupByURI(accessor, uri);
+ if (result < NULL)
+ {
+ // mapping found
+ return result;
+ }
+
+ return insertNew(accessor, uri, commitTime);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, long mappedId)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectByLongID, ReuseProbability.HIGH);
+ stmt.setLong(1, mappedId);
+ resultSet = stmt.executeQuery();
+
+ if (!resultSet.next())
+ {
+ OM.LOG.error("External ID " + mappedId + " not found. Database inconsistent!");
+ throw new IllegalStateException("External ID " + mappedId + " not found. Database inconsistent!");
+ }
+
+ return resultSet.getString(1);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ String where = " WHERE " + timestampField + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
+ DBUtil.serializeTable(out, connection, table, null, where);
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ DBUtil.deserializeTable(in, connection, table, monitor);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+
+ IDBStore store = idHandler.getStore();
+ table = store.getDBSchema().addTable("cdo_external_refs"); //$NON-NLS-1$
+ idField = table.addField("id", idHandler.getDBType()); //$NON-NLS-1$
+ uriField = table.addField("uri", DBType.VARCHAR, 1024); //$NON-NLS-1$
+ timestampField = table.addField("committime", DBType.BIGINT); //$NON-NLS-1$
+
+ table.addIndex(IDBIndex.Type.PRIMARY_KEY, idField);
+ table.addIndex(IDBIndex.Type.NON_UNIQUE, uriField);
+
+ IDBStoreAccessor writer = store.getWriter(null);
+ Connection connection = writer.getConnection();
+ Statement statement = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ statement = connection.createStatement();
+ store.getDBAdapter().createTable(table, statement);
+ connection.commit();
+
+ String sql = "SELECT MIN(" + idField + ") FROM " + table;
+ resultSet = statement.executeQuery(sql);
+
+ if (resultSet.next())
+ {
+ lastMappedID.set(resultSet.getLong(1));
+ }
+
+ // else: resultSet is empty => table is empty
+ // and lastMappedId stays 0 - as initialized.
+ }
+ catch (SQLException ex)
+ {
+ connection.rollback();
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(statement);
+ writer.release();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("INSERT INTO ");
+ builder.append(table);
+ builder.append("(");
+ builder.append(idField);
+ builder.append(",");
+ builder.append(uriField);
+ builder.append(",");
+ builder.append(timestampField);
+ builder.append(") VALUES (?, ?, ?)");
+ sqlInsert = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(idField);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(table);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(uriField);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectByURI = builder.toString();
+
+ builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(uriField);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(table);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(idField);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlSelectByLongID = builder.toString();
+ }
+
+ private long insertNew(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ long newMappedID = lastMappedID.decrementAndGet();
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.MEDIUM);
+ stmt.setLong(1, newMappedID);
+ stmt.setString(2, uri);
+ stmt.setLong(3, commitTime);
+
+ DBUtil.update(stmt, true);
+ return newMappedID;
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private long lookupByURI(IDBStoreAccessor accessor, String uri)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlSelectByURI, ReuseProbability.HIGH);
+ stmt.setString(1, uri);
+
+ resultSet = stmt.executeQuery();
+
+ if (resultSet.next())
+ {
+ return resultSet.getLong(1);
+ }
+
+ // Not found ...
+ return NULL;
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private static IDBStoreAccessor getAccessor()
+ {
+ IStoreAccessor accessor = StoreThreadLocal.getAccessor();
+ if (accessor == null)
+ {
+ throw new IllegalStateException("Can only be called from within a valid IDBStoreAccessor context");
+ }
+
+ return (IDBStoreAccessor)accessor;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java
new file mode 100644
index 0000000000..49344d311c
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/IObjectTypeMapper.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+
+import java.io.IOException;
+import java.sql.Connection;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public interface IObjectTypeMapper
+{
+ public CDOClassifierRef getObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type);
+
+ public void removeObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ /**
+ * Return the maximum object id managed by this cache.
+ *
+ * @param connection
+ * the DB connection to use.
+ * @return the maximum object ID.
+ */
+ public CDOID getMaxID(Connection connection, IIDHandler idHandler);
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ public void rawImport(Connection connection, CDODataInput in, OMMonitor monitor) throws IOException;
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java
new file mode 100644
index 0000000000..79d06158c7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/LongIDHandler.java
@@ -0,0 +1,265 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDExternal;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+import org.eclipse.emf.cdo.spi.server.LongIDStore;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class LongIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final CDOID MIN = CDOID.NULL;
+
+ public static final CDOID MAX = create(Long.MAX_VALUE);
+
+ private DBStore store;
+
+ private ExternalReferenceManager externalReferenceManager;
+
+ private CDOID lastObjectID = MIN;
+
+ private CDOID nextLocalObjectID = MAX;
+
+ public LongIDHandler(DBStore store)
+ {
+ this.store = store;
+ externalReferenceManager = new ExternalReferenceManager(this);
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return LongIDStore.OBJECT_ID_TYPES;
+ }
+
+ public CDOID getMinCDOID()
+ {
+ return MIN;
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ return MAX;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ return id1.compareTo(id2);
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ Long id = Long.valueOf(val);
+ return create(id);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ return lastObjectID;
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ this.lastObjectID = lastObjectID;
+ }
+
+ public synchronized void adjustLastObjectID(CDOID maxID)
+ {
+ if (compare(maxID, lastObjectID) > 0)
+ {
+ lastObjectID = maxID;
+ }
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ return nextLocalObjectID;
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ this.nextLocalObjectID = nextLocalObjectID;
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ CDOID result = nextLocalObjectID;
+ nextLocalObjectID = create(value(result) - 1);
+ return result;
+ }
+
+ lastObjectID = create(value(lastObjectID) + 1);
+ return lastObjectID;
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ return compare(id, nextLocalObjectID) > 0;
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.BIGINT;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ long value = value(id);
+ builder.append(value);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ long value;
+ if (id.getType() == CDOID.Type.EXTERNAL_OBJECT)
+ {
+ if (commitTime == CDOBranchPoint.INVALID_DATE)
+ {
+ CommitContext commitContext = StoreThreadLocal.getCommitContext();
+ commitTime = commitContext != null ? commitContext.getBranchPoint().getTimeStamp()
+ : CDOBranchPoint.UNSPECIFIED_DATE; // Happens on rawStore for workspace checkouts
+ }
+
+ value = externalReferenceManager.mapExternalReference((CDOIDExternal)id, commitTime);
+ }
+ else
+ {
+ value = value(id);
+ }
+
+ stmt.setLong(column, value);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ long id = resultSet.getLong(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return unmapExternalReference(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ long id = resultSet.getLong(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return unmapExternalReference(id);
+ }
+
+ private CDOID unmapExternalReference(long id)
+ {
+ if (id < 0)
+ {
+ return externalReferenceManager.unmapExternalReference(id);
+ }
+
+ return create(id);
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return create(externalReferenceManager.mapURI(accessor, uri, commitTime));
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ if (id.getType() == CDOID.Type.EXTERNAL_OBJECT)
+ {
+ return ((CDOIDExternal)id).getURI();
+ }
+
+ return externalReferenceManager.unmapURI(accessor, value(id));
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ externalReferenceManager.rawExport(connection, out, fromCommitTime, toCommitTime);
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ externalReferenceManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ externalReferenceManager.activate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ externalReferenceManager.deactivate();
+ super.doDeactivate();
+ }
+
+ private static CDOID create(long id)
+ {
+ return CDOIDUtil.createLong(id);
+ }
+
+ private static long value(CDOID id)
+ {
+ return CDOIDUtil.getLong(id);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java
new file mode 100644
index 0000000000..8882555ac7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java
@@ -0,0 +1,429 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOModelConstants;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBRowHandler;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.Monitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * @author Eike Stepper
+ */
+public class MetaDataManager extends Lifecycle implements IMetaDataManager
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, MetaDataManager.class);
+
+ private static final boolean ZIP_PACKAGE_BYTES = true;
+
+ private IDBStore store;
+
+ private Map<EModelElement, CDOID> modelElementToMetaID = new HashMap<EModelElement, CDOID>();
+
+ private Map<CDOID, EModelElement> metaIDToModelElement = new HashMap<CDOID, EModelElement>();
+
+ public MetaDataManager(IDBStore store)
+ {
+ this.store = store;
+ }
+
+ public synchronized CDOID getMetaID(EModelElement modelElement, long commitTime)
+ {
+ CDOID metaID = modelElementToMetaID.get(modelElement);
+ if (metaID != null)
+ {
+ return metaID;
+ }
+
+ IDBStoreAccessor accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor();
+ String uri = EcoreUtil.getURI(modelElement).toString();
+ metaID = store.getIDHandler().mapURI(accessor, uri, commitTime);
+ cacheMetaIDMapping(modelElement, metaID);
+
+ return metaID;
+ }
+
+ public synchronized EModelElement getMetaInstance(CDOID id)
+ {
+ EModelElement modelElement = metaIDToModelElement.get(id);
+ if (modelElement != null)
+ {
+ return modelElement;
+ }
+
+ IDBStoreAccessor accessor = (IDBStoreAccessor)StoreThreadLocal.getAccessor();
+ String uri = store.getIDHandler().unmapURI(accessor, id);
+
+ ResourceSet resourceSet = new ResourceSetImpl();
+ resourceSet.setPackageRegistry(getStore().getRepository().getPackageRegistry());
+
+ return (EModelElement)resourceSet.getEObject(URI.createURI(uri), true);
+ }
+
+ public synchronized void clearMetaIDMappings()
+ {
+ modelElementToMetaID.clear();
+ metaIDToModelElement.clear();
+ }
+
+ public final EPackage[] loadPackageUnit(Connection connection, InternalCDOPackageUnit packageUnit)
+ {
+ String where = CDODBSchema.PACKAGE_UNITS_ID.getName() + "='" + packageUnit.getID() + "'"; //$NON-NLS-1$ //$NON-NLS-2$
+ Object[] values = DBUtil.select(connection, where, CDODBSchema.PACKAGE_UNITS_PACKAGE_DATA);
+ byte[] bytes = (byte[])values[0];
+ EPackage ePackage = createEPackage(packageUnit, bytes);
+ return EMFUtil.getAllPackages(ePackage);
+ }
+
+ public Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection)
+ {
+ return readPackageUnits(connection, CDOBranchPoint.UNSPECIFIED_DATE, CDOBranchPoint.UNSPECIFIED_DATE, new Monitor());
+ }
+
+ public final void writePackageUnits(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(2);
+ fillSystemTables(connection, packageUnits, monitor.fork());
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Export package units
+ String where = " WHERE p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.CORE_PACKAGE_URI + //
+ "' AND p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.RESOURCE_PACKAGE_URI + //
+ "' AND p_u." + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.TYPES_PACKAGE_URI + //
+ "' AND p_u." + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
+ DBUtil.serializeTable(out, connection, CDODBSchema.PACKAGE_UNITS, "p_u", where);
+
+ // Export package infos
+ String join = ", " + CDODBSchema.PACKAGE_UNITS + " p_u" + where + " AND p_i." + CDODBSchema.PACKAGE_INFOS_UNIT
+ + "=p_u." + CDODBSchema.PACKAGE_UNITS_ID;
+ DBUtil.serializeTable(out, connection, CDODBSchema.PACKAGE_INFOS, "p_i", join);
+ }
+
+ public Collection<InternalCDOPackageUnit> rawImport(Connection connection, CDODataInput in, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor) throws IOException
+ {
+ monitor.begin(3);
+
+ try
+ {
+ DBUtil.deserializeTable(in, connection, CDODBSchema.PACKAGE_UNITS, monitor.fork());
+ DBUtil.deserializeTable(in, connection, CDODBSchema.PACKAGE_INFOS, monitor.fork());
+ return readPackageUnits(connection, fromCommitTime, toCommitTime, monitor.fork());
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ protected IDBStore getStore()
+ {
+ return store;
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ checkState(store, "Store is not set"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ clearMetaIDMappings();
+ super.doDeactivate();
+ }
+
+ protected InternalCDOPackageInfo createPackageInfo()
+ {
+ return (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo();
+ }
+
+ protected InternalCDOPackageUnit createPackageUnit()
+ {
+ return (InternalCDOPackageUnit)CDOModelUtil.createPackageUnit();
+ }
+
+ private InternalCDOPackageRegistry getPackageRegistry()
+ {
+ return (InternalCDOPackageRegistry)store.getRepository().getPackageRegistry();
+ }
+
+ private EPackage createEPackage(InternalCDOPackageUnit packageUnit, byte[] bytes)
+ {
+ ResourceSet resourceSet = EMFUtil.newEcoreResourceSet(getPackageRegistry());
+ return EMFUtil.createEPackage(packageUnit.getID(), bytes, ZIP_PACKAGE_BYTES, resourceSet, false);
+ }
+
+ private byte[] getEPackageBytes(InternalCDOPackageUnit packageUnit)
+ {
+ EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage();
+ return EMFUtil.getEPackageBytes(ePackage, ZIP_PACKAGE_BYTES, getPackageRegistry());
+ }
+
+ private void fillSystemTables(Connection connection, InternalCDOPackageUnit packageUnit, OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing package unit: {0}", packageUnit); //$NON-NLS-1$
+ }
+
+ InternalCDOPackageInfo[] packageInfos = packageUnit.getPackageInfos();
+ Async async = null;
+ monitor.begin(1 + packageInfos.length);
+
+ try
+ {
+ String sql = "INSERT INTO " + CDODBSchema.PACKAGE_UNITS + " VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$
+ DBUtil.trace(sql);
+ PreparedStatement stmt = null;
+
+ try
+ {
+ async = monitor.forkAsync();
+ stmt = connection.prepareStatement(sql);
+ stmt.setString(1, packageUnit.getID());
+ stmt.setInt(2, packageUnit.getOriginalType().ordinal());
+ stmt.setLong(3, packageUnit.getTimeStamp());
+ stmt.setBytes(4, getEPackageBytes(packageUnit));
+
+ if (stmt.execute())
+ {
+ throw new DBException("No result set expected"); //$NON-NLS-1$
+ }
+
+ if (stmt.getUpdateCount() == 0)
+ {
+ throw new DBException("No row inserted into table " + CDODBSchema.PACKAGE_UNITS); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(stmt);
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+
+ for (InternalCDOPackageInfo packageInfo : packageInfos)
+ {
+ fillSystemTables(connection, packageInfo, monitor); // Don't fork monitor
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void fillSystemTables(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(packageUnits.length);
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ fillSystemTables(connection, packageUnit, monitor.fork());
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private void fillSystemTables(Connection connection, InternalCDOPackageInfo packageInfo, OMMonitor monitor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing package info: {0}", packageInfo); //$NON-NLS-1$
+ }
+
+ String packageURI = packageInfo.getPackageURI();
+ String parentURI = packageInfo.getParentURI();
+ String unitID = packageInfo.getPackageUnit().getID();
+
+ String sql = "INSERT INTO " + CDODBSchema.PACKAGE_INFOS + " VALUES (?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$
+ DBUtil.trace(sql);
+ PreparedStatement stmt = null;
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ stmt = connection.prepareStatement(sql);
+ stmt.setString(1, packageURI);
+ stmt.setString(2, parentURI);
+ stmt.setString(3, unitID);
+
+ if (stmt.execute())
+ {
+ throw new DBException("No result set expected"); //$NON-NLS-1$
+ }
+
+ if (stmt.getUpdateCount() == 0)
+ {
+ throw new DBException("No row inserted into table " + CDODBSchema.PACKAGE_INFOS); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(stmt);
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+
+ private Collection<InternalCDOPackageUnit> readPackageUnits(Connection connection, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor)
+ {
+ final Map<String, InternalCDOPackageUnit> packageUnits = new HashMap<String, InternalCDOPackageUnit>();
+ IDBRowHandler unitRowHandler = new IDBRowHandler()
+ {
+ public boolean handle(int row, final Object... values)
+ {
+ InternalCDOPackageUnit packageUnit = createPackageUnit();
+ packageUnit.setOriginalType(CDOPackageUnit.Type.values()[(Integer)values[1]]);
+ packageUnit.setTimeStamp((Long)values[2]);
+ packageUnits.put((String)values[0], packageUnit);
+ return true;
+ }
+ };
+
+ String where = null;
+ if (fromCommitTime != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ where = CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.CORE_PACKAGE_URI + "' AND "
+ + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.RESOURCE_PACKAGE_URI + "' AND "
+ + CDODBSchema.PACKAGE_UNITS_ID + "<>'" + CDOModelConstants.TYPES_PACKAGE_URI + "' AND "
+ + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
+ }
+
+ DBUtil.select(connection, unitRowHandler, where, CDODBSchema.PACKAGE_UNITS_ID,
+ CDODBSchema.PACKAGE_UNITS_ORIGINAL_TYPE, CDODBSchema.PACKAGE_UNITS_TIME_STAMP);
+
+ final Map<String, List<InternalCDOPackageInfo>> packageInfos = new HashMap<String, List<InternalCDOPackageInfo>>();
+ IDBRowHandler infoRowHandler = new IDBRowHandler()
+ {
+ public boolean handle(int row, final Object... values)
+ {
+ InternalCDOPackageInfo packageInfo = createPackageInfo();
+ packageInfo.setPackageURI((String)values[1]);
+ packageInfo.setParentURI((String)values[2]);
+
+ String unit = (String)values[0];
+ List<InternalCDOPackageInfo> list = packageInfos.get(unit);
+ if (list == null)
+ {
+ list = new ArrayList<InternalCDOPackageInfo>();
+ packageInfos.put(unit, list);
+ }
+
+ list.add(packageInfo);
+ return true;
+ }
+ };
+
+ monitor.begin();
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ DBUtil.select(connection, infoRowHandler, CDODBSchema.PACKAGE_INFOS_UNIT, CDODBSchema.PACKAGE_INFOS_URI,
+ CDODBSchema.PACKAGE_INFOS_PARENT);
+ }
+ finally
+ {
+ async.stop();
+ monitor.done();
+ }
+
+ for (Entry<String, InternalCDOPackageUnit> entry : packageUnits.entrySet())
+ {
+ String id = entry.getKey();
+ InternalCDOPackageUnit packageUnit = entry.getValue();
+
+ List<InternalCDOPackageInfo> list = packageInfos.get(id);
+ InternalCDOPackageInfo[] array = list.toArray(new InternalCDOPackageInfo[list.size()]);
+ packageUnit.setPackageInfos(array);
+ }
+
+ return packageUnits.values();
+ }
+
+ private void cacheMetaIDMapping(EModelElement modelElement, CDOID metaID)
+ {
+ modelElementToMetaID.put(modelElement, metaID);
+ metaIDToModelElement.put(metaID, modelElement);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java
new file mode 100644
index 0000000000..86f189482b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/NullPreparedStatementCache.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashSet;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public class NullPreparedStatementCache extends AbstractPreparedStatementCache
+{
+ private HashSet<PreparedStatement> allocatedStatements = new HashSet<PreparedStatement>();
+
+ public NullPreparedStatementCache()
+ {
+ }
+
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability)
+ {
+ try
+ {
+ PreparedStatement result = getConnection().prepareStatement(sql);
+ allocatedStatements.add(result);
+ return result;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ public void releasePreparedStatement(PreparedStatement ps)
+ {
+ allocatedStatements.remove(ps);
+ DBUtil.close(ps);
+ }
+
+ @Override
+ protected void doBeforeDeactivate() throws Exception
+ {
+ if (!allocatedStatements.isEmpty())
+ {
+ OM.LOG.warn("Possible Leak Detected:"); //$NON-NLS-1$
+ for (PreparedStatement ps : allocatedStatements)
+ {
+ OM.LOG.warn("- " + ps.toString()); //$NON-NLS-1$
+ }
+
+ assert false;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java
new file mode 100644
index 0000000000..9328e5c633
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/ObjectIDIterator.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Victor Roldan Betancort - bug 208689
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.NoSuchElementException;
+
+/**
+ * @author Eike Stepper
+ */
+public abstract class ObjectIDIterator implements CloseableIterator<CDOID>
+{
+ private IMappingStrategy mappingStrategy;
+
+ private IIDHandler idHandler;
+
+ private IDBStoreAccessor accessor;
+
+ private ResultSet currentResultSet;
+
+ private CDOID nextID;
+
+ private boolean closed;
+
+ /**
+ * Creates an iterator over all objects in a store. It is important to {@link #close()} of this iterator after usage
+ * to properly close internal result sets.
+ */
+ public ObjectIDIterator(IMappingStrategy mappingStrategy, IDBStoreAccessor accessor)
+ {
+ this.mappingStrategy = mappingStrategy;
+ this.accessor = accessor;
+ idHandler = getMappingStrategy().getStore().getIDHandler();
+
+ }
+
+ public void close()
+ {
+ closeCurrentResultSet();
+ nextID = null;
+ closed = true;
+ }
+
+ public boolean isClosed()
+ {
+ return closed;
+ }
+
+ public IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public IDBStoreAccessor getAccessor()
+ {
+ return accessor;
+ }
+
+ public boolean hasNext()
+ {
+ if (closed)
+ {
+ return false;
+ }
+
+ nextID = null;
+ for (;;)
+ {
+ if (currentResultSet == null)
+ {
+ currentResultSet = getNextResultSet();
+ if (currentResultSet == null)
+ {
+ return false;
+ }
+ }
+
+ try
+ {
+ if (currentResultSet.next())
+ {
+ nextID = idHandler.getCDOID(currentResultSet, 1);
+ return true;
+ }
+
+ closeCurrentResultSet();
+
+ currentResultSet = null;
+ return false;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+ }
+
+ protected void closeCurrentResultSet()
+ {
+ DBUtil.close(currentResultSet);
+ }
+
+ public CDOID next()
+ {
+ if (nextID == null)
+ {
+ throw new NoSuchElementException();
+ }
+
+ return nextID;
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ protected abstract ResultSet getNextResultSet();
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java
new file mode 100644
index 0000000000..589e7a1ed7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SQLQueryHandler.java
@@ -0,0 +1,364 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kai Schlamp - initial API and implementation
+ * Eike Stepper - maintenance
+ * Kai Schlamp - Bug 284812: [DB] Query non CDO object fails
+ * Erdal Karaca - added cdoObjectResultAsMap parameter to return Map<String,Object> in result
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
+import org.eclipse.emf.cdo.server.IQueryContext;
+import org.eclipse.emf.cdo.server.IQueryHandler;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements server side SQL query execution.
+ *
+ * @author Kai Schlamp
+ */
+public class SQLQueryHandler implements IQueryHandler
+{
+ public static final String QUERY_LANGUAGE = "sql";
+
+ public static final String FIRST_RESULT = "firstResult";
+
+ public static final String CDO_OBJECT_QUERY = "cdoObjectQuery";
+
+ public static final String MAP_QUERY = "mapQuery";
+
+ public static final String QUERY_STATEMENT = "queryStatement";
+
+ private DBStoreAccessor storeAccessor;
+
+ public SQLQueryHandler(DBStoreAccessor storeAccessor)
+ {
+ this.storeAccessor = storeAccessor;
+ }
+
+ public DBStoreAccessor getStoreAccessor()
+ {
+ return storeAccessor;
+ }
+
+ /**
+ * Executes SQL queries. Gets the connection from {@link DBStoreAccessor}, creates a SQL query and sets the parameters
+ * taken from the {@link CDOQueryInfo#getParameters()}.
+ * <p>
+ * Takes into account the {@link CDOQueryInfo#getMaxResults()} and the {@link SQLQueryHandler#FIRST_RESULT} (numbered
+ * from 0) values for paging.
+ * <p>
+ * By default (parameter {@link SQLQueryHandler#CDO_OBJECT_QUERY} == true) a query for CDO Objects is exectued. The
+ * SQL query must return the CDO ID in the first column for this to work. If you set
+ * {@link SQLQueryHandler#CDO_OBJECT_QUERY} parameter to false, the value of the first column of a row itself is
+ * returned.
+ * <p>
+ * By default (parameter {@link SQLQueryHandler#QUERY_STATEMENT} == true) query statements are executed. Set this
+ * parameter to false for update/DDL statements.
+ * <p>
+ * It is possible to use variables inside the SQL string with ":" as prefix. E.g.
+ * "SELECT cdo_id FROM Company WHERE name LIKE :name". The value must then be set by using a parameter. E.g.
+ * query.setParameter(":name", "Foo%");
+ *
+ * @param info
+ * the object containing the query and parameters
+ * @param context
+ * the query results are placed in the context
+ * @see IQueryHandler#executeQuery(CDOQueryInfo, IQueryContext)
+ */
+ public void executeQuery(CDOQueryInfo info, IQueryContext context)
+ {
+ String language = info.getQueryLanguage();
+ if (!QUERY_LANGUAGE.equals(language))
+ {
+ throw new IllegalArgumentException("Unsupported query language: " + language);
+ }
+
+ IIDHandler idHandler = storeAccessor.getStore().getIDHandler();
+ Connection connection = storeAccessor.getConnection();
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ String query = info.getQueryString();
+
+ try
+ {
+ int firstResult = -1;
+ boolean queryStatement = true;
+ boolean objectQuery = true;
+ boolean mapQuery = false;
+
+ HashMap<String, List<Integer>> paramMap = new HashMap<String, List<Integer>>();
+ query = parse(query, paramMap);
+ statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+
+ for (String key : info.getParameters().keySet())
+ {
+ if (FIRST_RESULT.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ firstResult = (Integer)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + FIRST_RESULT + " must be an integer but it is a " + o
+ + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (QUERY_STATEMENT.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ queryStatement = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + QUERY_STATEMENT + " must be an boolean but it is a "
+ + o + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (CDO_OBJECT_QUERY.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ objectQuery = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + CDO_OBJECT_QUERY + " must be a boolean but it is a "
+ + o + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (MAP_QUERY.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ mapQuery = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + MAP_QUERY + " must be a boolean but it is a " + o
+ + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else
+ {
+ if (!paramMap.containsKey(key) || paramMap.get(key) == null)
+ {
+ throw new IllegalArgumentException("No parameter value found for named parameter " + key);
+ }
+
+ Integer[] indexes = paramMap.get(key).toArray(new Integer[0]);
+ for (int i = 0; i < indexes.length; i++)
+ {
+ Object parameter = info.getParameters().get(key);
+ statement.setObject(indexes[i], parameter);
+ }
+ }
+ }
+
+ if (queryStatement)
+ {
+ resultSet = statement.executeQuery();
+ if (firstResult > -1)
+ {
+ resultSet.absolute(firstResult);
+ }
+
+ String[] columnNames = null;
+ if (mapQuery)
+ {
+ columnNames = new String[resultSet.getMetaData().getColumnCount()];
+ for (int i = 1; i <= columnNames.length; i++)
+ {
+ columnNames[i - 1] = resultSet.getMetaData().getColumnName(i);
+ }
+ }
+
+ int maxResults = info.getMaxResults();
+ int counter = 0;
+
+ while (resultSet.next())
+ {
+ if (maxResults != CDOQueryInfo.UNLIMITED_RESULTS && counter++ >= maxResults)
+ {
+ break;
+ }
+
+ if (objectQuery)
+ {
+ CDOID result = idHandler.getCDOID(resultSet, 1);
+ context.addResult(result);
+ }
+ else
+ {
+ int columnCount = resultSet.getMetaData().getColumnCount();
+ if (columnCount == 1)
+ {
+ Object result = convertValue(resultSet.getObject(1));
+ context.addResult(mapQuery ? toMap(columnNames, new Object[] { result }) : result);
+ }
+ else
+ {
+ Object[] results = new Object[columnCount];
+ for (int i = 0; i < columnCount; i++)
+ {
+ results[i] = convertValue(resultSet.getObject(i + 1));
+ }
+
+ context.addResult(mapQuery ? toMap(columnNames, results) : results);
+ }
+ }
+ }
+ }
+ else
+ {
+ int result = statement.executeUpdate();
+ context.addResult(result);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException("Problem while executing SQL query: " + query, ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(statement);
+ }
+ }
+
+ private Object convertValue(Object value)
+ {
+ if (value instanceof Clob)
+ {
+ Clob clob = (Clob)value;
+
+ try
+ {
+ value = clob.getSubString(1, (int)clob.length());
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException("Could not extract CLOB value", ex);
+ }
+ }
+
+ return value;
+ }
+
+ private Map<String, Object> toMap(String[] columnNames, Object[] results)
+ {
+ Map<String, Object> ret = new HashMap<String, Object>();
+
+ for (int i = 0; i < columnNames.length; i++)
+ {
+ String columnName = columnNames[i];
+ ret.put(columnName, results[i]);
+ }
+
+ return ret;
+ }
+
+ private String parse(String query, Map<String, List<Integer>> paramMap)
+ {
+ int length = query.length();
+ StringBuilder builder = new StringBuilder(length);
+
+ boolean inSingleQuote = false;
+ boolean inDoubleQuote = false;
+ int index = 1;
+
+ for (int i = 0; i < length; i++)
+ {
+ char c = query.charAt(i);
+ if (inSingleQuote)
+ {
+ if (c == '\'')
+ {
+ inSingleQuote = false;
+ }
+ }
+ else if (inDoubleQuote)
+ {
+ if (c == '"')
+ {
+ inDoubleQuote = false;
+ }
+ }
+ else
+ {
+ if (c == '\'')
+ {
+ inSingleQuote = true;
+ }
+ else if (c == '"')
+ {
+ inDoubleQuote = true;
+ }
+ else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1)))
+ {
+ int j = i + 2;
+ while (j < length && Character.isJavaIdentifierPart(query.charAt(j)))
+ {
+ j++;
+ }
+
+ String name = query.substring(i + 1, j);
+ c = '?';
+ i += name.length();
+
+ List<Integer> indexList = paramMap.get(name);
+ if (indexList == null)
+ {
+ indexList = new ArrayList<Integer>();
+ paramMap.put(name, indexList);
+ }
+
+ indexList.add(new Integer(index));
+ index++;
+ }
+ }
+
+ builder.append(c);
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
new file mode 100644
index 0000000000..0ea0e9109b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.util.ImplementationError;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public class SmartPreparedStatementCache extends AbstractPreparedStatementCache
+{
+ private Cache cache;
+
+ private HashMap<PreparedStatement, CachedPreparedStatement> checkedOut = new HashMap<PreparedStatement, CachedPreparedStatement>();
+
+ public SmartPreparedStatementCache(int capacity)
+ {
+ cache = new Cache(capacity);
+ }
+
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability)
+ {
+ CachedPreparedStatement cachedStatement = cache.remove(sql);
+ if (cachedStatement == null)
+ {
+ cachedStatement = createCachedPreparedStatement(sql, reuseProbability);
+ }
+
+ PreparedStatement result = cachedStatement.getPreparedStatement();
+ checkedOut.put(result, cachedStatement);
+
+ return result;
+ }
+
+ /**
+ * @param ps
+ * the prepared statement to be released to the cache, or <code>null</code>.
+ */
+ public void releasePreparedStatement(PreparedStatement ps)
+ {
+ if (ps != null) // Bug 276926: Silently accept ps == null and do nothing.
+ {
+ CachedPreparedStatement cachedStatement = checkedOut.remove(ps);
+ cache.put(cachedStatement);
+ }
+ }
+
+ @Override
+ protected void doBeforeDeactivate() throws Exception
+ {
+ if (!checkedOut.isEmpty())
+ {
+ OM.LOG.warn("Statement leak detected"); //$NON-NLS-1$
+ }
+ }
+
+ private CachedPreparedStatement createCachedPreparedStatement(String sql, ReuseProbability reuseProbability)
+ {
+ try
+ {
+ Connection connection = getConnection();
+ PreparedStatement stmt = connection.prepareStatement(sql);
+ return new CachedPreparedStatement(sql, reuseProbability, stmt);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private static final class Cache
+ {
+ private CacheList lists[];
+
+ private HashMap<String, CachedPreparedStatement> lookup;
+
+ private int capacity;
+
+ public Cache(int capacity)
+ {
+ this.capacity = capacity;
+
+ lookup = new HashMap<String, CachedPreparedStatement>(capacity);
+
+ lists = new CacheList[ReuseProbability.values().length];
+ for (ReuseProbability prob : ReuseProbability.values())
+ {
+ lists[prob.ordinal()] = new CacheList();
+ }
+ }
+
+ public void put(CachedPreparedStatement cachedStatement)
+ {
+ // refresh age
+ cachedStatement.touch();
+
+ // put into appripriate list
+ lists[cachedStatement.getProbability().ordinal()].add(cachedStatement);
+
+ // put into lookup table
+ if (lookup.put(cachedStatement.getSQL(), cachedStatement) != null)
+ {
+ throw new ImplementationError(cachedStatement.getSQL() + " already in cache"); //$NON-NLS-1$
+ }
+
+ // handle capacity overflow
+ if (lookup.size() > capacity)
+ {
+ evictOne();
+ }
+ }
+
+ private void evictOne()
+ {
+ long maxAge = -1;
+ int ordinal = -1;
+
+ for (ReuseProbability prob : ReuseProbability.values())
+ {
+ if (!lists[prob.ordinal()].isEmpty())
+ {
+ long age = lists[prob.ordinal()].tail().getAge();
+ if (maxAge < age)
+ {
+ maxAge = age;
+ ordinal = prob.ordinal();
+ }
+ }
+ }
+
+ remove(lists[ordinal].tail().getSQL());
+ }
+
+ public CachedPreparedStatement remove(String sql)
+ {
+ CachedPreparedStatement result = lookup.remove(sql);
+ if (result == null)
+ {
+ return null;
+ }
+
+ lists[result.getProbability().ordinal()].remove(result);
+ return result;
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class CacheList
+ {
+ private CachedPreparedStatement first;
+
+ private CachedPreparedStatement last;
+
+ public CacheList()
+ {
+ }
+
+ public void add(CachedPreparedStatement s)
+ {
+ if (first == null)
+ {
+ first = s;
+ last = s;
+ s.previous = null;
+ s.next = null;
+ }
+ else
+ {
+ first.previous = s;
+ s.next = first;
+ first = s;
+ }
+ }
+
+ public void remove(CachedPreparedStatement s)
+ {
+ if (s == first)
+ {
+ first = s.next;
+ }
+
+ if (s.next != null)
+ {
+ s.next.previous = s.previous;
+ }
+
+ if (s == last)
+ {
+ last = s.previous;
+ }
+
+ if (s.previous != null)
+ {
+ s.previous.next = s.next;
+ }
+
+ s.previous = null;
+ s.next = null;
+ }
+
+ public CachedPreparedStatement tail()
+ {
+ return last;
+ }
+
+ public boolean isEmpty()
+ {
+ return first == null;
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private static final class CachedPreparedStatement
+ {
+ private long timeStamp;
+
+ private String sql;
+
+ private ReuseProbability probability;
+
+ private PreparedStatement statement;
+
+ /**
+ * DL field
+ */
+ private CachedPreparedStatement previous;
+
+ /**
+ * DL field
+ */
+ private CachedPreparedStatement next;
+
+ public CachedPreparedStatement(String sql, ReuseProbability prob, PreparedStatement stmt)
+ {
+ this.sql = sql;
+ probability = prob;
+ statement = stmt;
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public PreparedStatement getPreparedStatement()
+ {
+ return statement;
+ }
+
+ public long getAge()
+ {
+ long currentTime = System.currentTimeMillis();
+ return (currentTime - timeStamp) * probability.ordinal();
+ }
+
+ public void touch()
+ {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public String getSQL()
+ {
+ return sql;
+ }
+
+ public ReuseProbability getProbability()
+ {
+ return probability;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java
new file mode 100644
index 0000000000..803958428d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java
@@ -0,0 +1,249 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class StringIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(CDOID.ObjectType.STRING);
+
+ public static final CDOID MIN = CDOID.NULL;
+
+ public static final CDOID MAX = create(Long.toString(Long.MAX_VALUE));
+
+ private DBStore store;
+
+ private long lastObjectID = 0;
+
+ private long nextLocalObjectID = Long.MAX_VALUE;
+
+ public StringIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ if (id1.getType() == CDOID.Type.OBJECT && id2.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.valueOf(value(id1)).compareTo(Long.valueOf(value(id2)));
+ }
+
+ return id1.compareTo(id2);
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ return CDOIDUtil.createString("" + lastObjectID);
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ this.lastObjectID = Long.parseLong(value(lastObjectID));
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // TODO: implement StringIDHandler.adjustLastObjectID(maxID)
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID);
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ this.nextLocalObjectID = Long.parseLong(value(nextLocalObjectID));
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID--);
+ }
+
+ return CDOIDUtil.createString("" + ++lastObjectID);
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ if (id.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.parseLong(value(id)) > nextLocalObjectID;
+ }
+
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ String value = value(id);
+ stmt.setString(column, value == null || value.length() == 0 ? "0" : value);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ return MIN;
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ return MAX;
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return create(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return value(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == '0')
+ {
+ return null;
+ }
+
+ if (Character.isDigit(firstChar))
+ {
+ long value = Long.parseLong(id);
+ if (value < 0)
+ {
+ throw new IllegalArgumentException("Illegal ID value: " + id);
+ }
+
+ return CDOIDUtil.createString(id);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java
new file mode 100644
index 0000000000..088cf83c62
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java
@@ -0,0 +1,237 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class UUIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set<ObjectType> OBJECT_ID_TYPES = Collections.singleton(ObjectType.UUID);
+
+ private static final char NULL_CHAR = '0';
+
+ private static final String NULL_STRING = Character.toString(NULL_CHAR);
+
+ private static final char INTERNAL_CHAR = '@';
+
+ private static final String INTERNAL_STRING = Character.toString(INTERNAL_CHAR);
+
+ private DBStore store;
+
+ public UUIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set<ObjectType> getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(INTERNAL_STRING + val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ // Do nothing
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ stmt.setString(column, value(id));
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return CDOIDUtil.createExternal(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == NULL_CHAR)
+ {
+ return null;
+ }
+
+ if (firstChar == INTERNAL_CHAR)
+ {
+ byte[] bytes = CDOIDUtil.decodeUUID(id.substring(1));
+ return CDOIDUtil.createUUID(bytes);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ if (CDOIDUtil.isNull(id))
+ {
+ return NULL_STRING;
+ }
+
+ if (id.isExternal())
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ return INTERNAL_STRING + id.toURIFragment();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java
new file mode 100644
index 0000000000..d45d95c8f7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.bundle;
+
+import org.eclipse.net4j.util.om.OMBundle;
+import org.eclipse.net4j.util.om.OMPlatform;
+import org.eclipse.net4j.util.om.OSGiActivator;
+import org.eclipse.net4j.util.om.log.OMLogger;
+import org.eclipse.net4j.util.om.trace.OMTracer;
+
+/**
+ * The <em>Operations & Maintenance</em> class of this bundle.
+ *
+ * @author Eike Stepper
+ */
+public abstract class OM
+{
+ public static final String BUNDLE_ID = "org.eclipse.emf.cdo.server.db"; //$NON-NLS-1$
+
+ public static final OMBundle BUNDLE = OMPlatform.INSTANCE.bundle(BUNDLE_ID, OM.class);
+
+ public static final OMTracer DEBUG = BUNDLE.tracer("debug"); //$NON-NLS-1$
+
+ public static final OMLogger LOG = BUNDLE.logger();
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Activator extends OSGiActivator
+ {
+ public Activator()
+ {
+ super(BUNDLE);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java
new file mode 100644
index 0000000000..d4781d312a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.jdbc;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.sql.PreparedStatement;
+import java.text.MessageFormat;
+
+/**
+ * Wrapper for a prepared statement that is cleaned up when it is cached in a WeakReferenceCache and gc'd. Note that
+ * this is just a wrapper with access to its wrapped object. There's no interface delegation, because the interface
+ * delegation would also put the necessity to wrap resultSets and maybe even more, which seems to much overkill for a
+ * simple internal implementation.
+ *
+ * @author Stefan Winkler
+ */
+public class WrappedPreparedStatement
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, WrappedPreparedStatement.class);
+
+ private PreparedStatement wrappedStatement;
+
+ public WrappedPreparedStatement(PreparedStatement ps)
+ {
+ wrappedStatement = ps;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Wrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+ }
+
+ public PreparedStatement getWrappedStatement()
+ {
+ return wrappedStatement;
+ }
+
+ public PreparedStatement unwrapStatement()
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("UnWrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+
+ PreparedStatement result = wrappedStatement;
+ wrappedStatement = null;
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("Wrapped[{0}]", wrappedStatement); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ if (wrappedStatement != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Closing statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+
+ DBUtil.close(wrappedStatement);
+ wrappedStatement = null;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
new file mode 100644
index 0000000000..d9c76b325f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
@@ -0,0 +1,641 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 282976: [DB] Influence Mappings through EAnnotations
+ * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping
+ * Stefan Winkler - maintenance
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.ObjectIDIterator;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * This abstract base class implements those methods which are most likely common to most mapping strategies. It can be
+ * used to derive custom mapping strategy implementation.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class AbstractMappingStrategy extends Lifecycle implements IMappingStrategy
+{
+ // --------- database name generation strings --------------
+ protected static final String NAME_SEPARATOR = "_"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_FEATURE = "F"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_CLASS = "C"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_PACKAGE = "P"; //$NON-NLS-1$
+
+ protected static final String GENERAL_PREFIX = "X"; //$NON-NLS-1$
+
+ protected static final String GENERAL_SUFFIX = "0"; //$NON-NLS-1$
+
+ /**
+ * Prefix for unsettable feature helper columns
+ */
+ protected static final String CDO_SET_PREFIX = "cdo_set_"; //$NON-NLS-1$
+
+ protected static final String FEATURE_TABLE_SUFFIX = "_list"; //$NON-NLS-1$
+
+ private IDBStore store;
+
+ private Map<String, String> properties;
+
+ private ConcurrentMap<EClass, IClassMapping> classMappings;
+
+ private boolean allClassMappingsCreated;
+
+ public AbstractMappingStrategy()
+ {
+ classMappings = new ConcurrentHashMap<EClass, IClassMapping>();
+ }
+
+ // -- property related methods -----------------------------------------
+
+ public synchronized Map<String, String> getProperties()
+ {
+ if (properties == null)
+ {
+ properties = new HashMap<String, String>();
+ }
+
+ return properties;
+ }
+
+ public synchronized void setProperties(Map<String, String> properties)
+ {
+ this.properties = properties;
+ }
+
+ private int getMaxTableNameLength()
+ {
+ String value = getProperties().get(PROP_MAX_TABLE_NAME_LENGTH);
+ return value == null ? store.getDBAdapter().getMaxTableNameLength() : Integer.valueOf(value);
+ }
+
+ private int getMaxFieldNameLength()
+ {
+ String value = getProperties().get(PROP_MAX_FIELD_NAME_LENGTH);
+ return value == null ? store.getDBAdapter().getMaxFieldNameLength() : Integer.valueOf(value);
+ }
+
+ private boolean isQualifiedNames()
+ {
+ String value = getProperties().get(PROP_QUALIFIED_NAMES);
+ return value == null ? false : Boolean.valueOf(value);
+ }
+
+ private boolean isForceNamesWithID()
+ {
+ String value = getProperties().get(PROP_FORCE_NAMES_WITH_ID);
+ return value == null ? false : Boolean.valueOf(value);
+ }
+
+ private String getTableNamePrefix()
+ {
+ String value = getProperties().get(PROP_TABLE_NAME_PREFIX);
+ return StringUtil.safe(value);
+ }
+
+ // -- getters and setters ----------------------------------------------
+
+ public final IDBStore getStore()
+ {
+ return store;
+ }
+
+ public final void setStore(IDBStore dbStore)
+ {
+ checkInactive();
+ store = dbStore;
+ }
+
+ protected final IMetaDataManager getMetaDataManager()
+ {
+ return getStore().getMetaDataManager();
+ }
+
+ // -- object id related methods ----------------------------------------
+
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler)
+ {
+ if (eClass == null)
+ {
+ Collection<IClassMapping> values = getClassMappings().values();
+ for (IClassMapping mapping : values)
+ {
+ mapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler);
+ }
+ }
+ else
+ {
+ IClassMapping classMapping = getClassMapping(eClass);
+ classMapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler);
+ }
+ }
+
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments)
+ {
+ Set<CDOID> result = new HashSet<CDOID>();
+ Collection<IClassMapping> classMappings = getClassMappings().values();
+
+ monitor.begin(classMappings.size());
+
+ try
+ {
+ for (IClassMapping mapping : classMappings)
+ {
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ Set<CDOID> ids = mapping.readChangeSet(accessor, segments);
+ result.addAll(ids);
+ }
+ finally
+ {
+ async.stop();
+ }
+ }
+
+ return result;
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public CloseableIterator<CDOID> readObjectIDs(IDBStoreAccessor accessor)
+ {
+ Collection<EClass> classes = getClassesWithObjectInfo();
+ final Iterator<EClass> classIt = classes.iterator();
+
+ return new ObjectIDIterator(this, accessor)
+ {
+ private PreparedStatement currentStatement;
+
+ @Override
+ protected ResultSet getNextResultSet()
+ {
+ while (classIt.hasNext())
+ {
+ EClass eClass = classIt.next();
+ IClassMapping mapping = getClassMapping(eClass);
+ currentStatement = mapping.createObjectIDStatement(getAccessor());
+
+ ResultSet resultSet = null;
+
+ try
+ {
+ resultSet = currentStatement.executeQuery();
+ return resultSet;
+ }
+ catch (Exception ex)
+ {
+ DBUtil.close(resultSet); // only on error
+ releaseCurrentStatement();
+ throw new DBException(ex);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void closeCurrentResultSet()
+ {
+ super.closeCurrentResultSet();
+ releaseCurrentStatement();
+ }
+
+ private void releaseCurrentStatement()
+ {
+ IPreparedStatementCache statementCache = getAccessor().getStatementCache();
+ statementCache.releasePreparedStatement(currentStatement);
+ currentStatement = null;
+ }
+ };
+ }
+
+ protected abstract Collection<EClass> getClassesWithObjectInfo();
+
+ // -- database name demangling methods ---------------------------------
+
+ public String getTableName(ENamedElement element)
+ {
+ String name = null;
+ String typePrefix = null;
+
+ if (element instanceof EClass)
+ {
+ typePrefix = TYPE_PREFIX_CLASS;
+ name = DBAnnotation.TABLE_NAME.getValue(element);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName((EClass)element, NAME_SEPARATOR) : element.getName();
+ }
+ }
+ else if (element instanceof EPackage)
+ {
+ typePrefix = TYPE_PREFIX_PACKAGE;
+ name = DBAnnotation.TABLE_NAME.getValue(element);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName((EPackage)element, NAME_SEPARATOR) : element.getName();
+ }
+ }
+ else
+ {
+ throw new ImplementationError("Unknown element: " + element); //$NON-NLS-1$
+ }
+
+ String prefix = getTableNamePrefix();
+ if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR))
+ {
+ prefix += NAME_SEPARATOR;
+ }
+
+ return getName(prefix + name, typePrefix + getUniqueID(element), getMaxTableNameLength());
+ }
+
+ public String getTableName(EClass eClass, EStructuralFeature feature)
+ {
+ String name = DBAnnotation.TABLE_NAME.getValue(eClass);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName(eClass, NAME_SEPARATOR) : eClass.getName();
+ }
+
+ name += NAME_SEPARATOR;
+ name += feature.getName();
+ name += FEATURE_TABLE_SUFFIX;
+
+ String prefix = getTableNamePrefix();
+ if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR))
+ {
+ prefix += NAME_SEPARATOR;
+ }
+
+ return getName(prefix + name, TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxTableNameLength());
+ }
+
+ public String getFieldName(EStructuralFeature feature)
+ {
+ String name = DBAnnotation.COLUMN_NAME.getValue(feature);
+ if (name == null)
+ {
+ name = getName(feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxFieldNameLength());
+ }
+
+ return name;
+ }
+
+ public String getUnsettableFieldName(EStructuralFeature feature)
+ {
+ String name = DBAnnotation.COLUMN_NAME.getValue(feature);
+ if (name != null)
+ {
+ return CDO_SET_PREFIX + name;
+ }
+
+ return getName(CDO_SET_PREFIX + feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature),
+ getMaxFieldNameLength());
+ }
+
+ private String getName(String name, String suffix, int maxLength)
+ {
+ if (!store.getDBAdapter().isValidFirstChar(name.charAt(0)))
+ {
+ name = GENERAL_PREFIX + name;
+ }
+
+ boolean forceNamesWithID = isForceNamesWithID();
+ if (!forceNamesWithID && store.getDBAdapter().isReservedWord(name))
+ {
+ name = name + GENERAL_SUFFIX;
+ }
+
+ if (name.length() > maxLength || forceNamesWithID)
+ {
+ suffix = NAME_SEPARATOR + suffix.replace('-', 'S');
+ int length = Math.min(name.length(), maxLength - suffix.length());
+ name = name.substring(0, length) + suffix;
+ }
+
+ return name;
+ }
+
+ private String getUniqueID(ENamedElement element)
+ {
+ long timeStamp;
+ CommitContext commitContext = StoreThreadLocal.getCommitContext();
+ if (commitContext != null)
+ {
+ timeStamp = commitContext.getBranchPoint().getTimeStamp();
+ }
+ else
+ {
+ // This happens outside a commit, i.e. at system init time.
+ // Ensure that resulting ext refs are not replicated!
+ timeStamp = CDOBranchPoint.INVALID_DATE;
+
+ // timeStamp = getStore().getRepository().getTimeStamp();
+ }
+
+ CDOID result = getMetaDataManager().getMetaID(element, timeStamp);
+
+ StringBuilder builder = new StringBuilder();
+ CDOIDUtil.write(builder, result);
+ return builder.toString();
+ }
+
+ // -- factories for mapping of classes, values, lists ------------------
+
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin();
+
+ try
+ {
+ async = monitor.forkAsync();
+
+ try
+ {
+ mapPackageUnits(packageUnits, connection, false);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits)
+ {
+ mapPackageUnits(packageUnits, connection, true);
+ }
+
+ private void mapPackageUnits(InternalCDOPackageUnit[] packageUnits, Connection connection, boolean unmap)
+ {
+ if (packageUnits != null && packageUnits.length != 0)
+ {
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ mapPackageInfos(packageUnit.getPackageInfos(), connection, unmap);
+ }
+ }
+ }
+
+ private void mapPackageInfos(InternalCDOPackageInfo[] packageInfos, Connection connection, boolean unmap)
+ {
+ boolean supportingEcore = getStore().getRepository().isSupportingEcore();
+ for (InternalCDOPackageInfo packageInfo : packageInfos)
+ {
+ EPackage ePackage = packageInfo.getEPackage();
+ if (!CDOModelUtil.isCorePackage(ePackage) || supportingEcore)
+ {
+ mapClasses(connection, unmap, EMFUtil.getPersistentClasses(ePackage));
+ }
+ }
+ }
+
+ private void mapClasses(Connection connection, boolean unmap, EClass... eClasses)
+ {
+ for (EClass eClass : eClasses)
+ {
+ if (!(eClass.isInterface() || eClass.isAbstract()))
+ {
+ String mappingAnnotation = DBAnnotation.TABLE_MAPPING.getValue(eClass);
+
+ // TODO Maybe we should explicitly report unknown values of the annotation
+ if (mappingAnnotation != null && mappingAnnotation.equalsIgnoreCase(DBAnnotation.TABLE_MAPPING_NONE))
+ {
+ continue;
+ }
+
+ if (!unmap)
+ {
+ // TODO Bugzilla 296087: Before we go ahead with creation, we should check if it's already there
+ IClassMapping mapping = createClassMapping(eClass);
+ getStore().getDBAdapter().createTables(mapping.getDBTables(), connection);
+ }
+ else
+ {
+ IClassMapping mapping = removeClassMapping(eClass);
+ getStore().getDBAdapter().dropTables(mapping.getDBTables(), connection);
+ }
+ }
+ }
+ }
+
+ private IClassMapping createClassMapping(EClass eClass)
+ {
+ IClassMapping mapping = doCreateClassMapping(eClass);
+ if (mapping != null)
+ {
+ classMappings.put(eClass, mapping);
+ }
+
+ return mapping;
+ }
+
+ private IClassMapping removeClassMapping(EClass eClass)
+ {
+ IClassMapping mapping = classMappings.get(eClass);
+ if (mapping != null)
+ {
+ IDBSchema schema = getStore().getDBSchema();
+ for (IDBTable table : mapping.getDBTables())
+ {
+ schema.removeTable(table.getName());
+ }
+ classMappings.remove(eClass);
+ }
+ return mapping;
+ }
+
+ protected abstract IClassMapping doCreateClassMapping(EClass eClass);
+
+ public final IClassMapping getClassMapping(EClass eClass)
+ {
+ if (!isMapped(eClass))
+ {
+ throw new IllegalArgumentException("Class is not mapped: " + eClass);
+ }
+
+ // Try without synchronization first; this will almost always succeed, so it avoids the
+ // performance penalty of syncing in the majority of cases
+ IClassMapping result = classMappings.get(eClass);
+ if (result == null)
+ {
+ // Synchronize on the classMappings to prevent concurrent invocations of createClassMapping
+ // (Synchronizing on the eClass allows for more concurrency, but is risky because application
+ // code may be syncing on the eClass also.)
+ synchronized (classMappings)
+ {
+ // Check again, because other thread may have just added the mapping
+ result = classMappings.get(eClass);
+ if (result == null)
+ {
+ result = createClassMapping(eClass);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public final Map<EClass, IClassMapping> getClassMappings()
+ {
+ return getClassMappings(true);
+ }
+
+ public final Map<EClass, IClassMapping> getClassMappings(boolean createOnDemand)
+ {
+ return doGetClassMappings(createOnDemand);
+ }
+
+ public final Map<EClass, IClassMapping> doGetClassMappings(boolean createOnDemand)
+ {
+ if (createOnDemand)
+ {
+ synchronized (classMappings)
+ {
+ if (!allClassMappingsCreated)
+ {
+ createAllClassMappings();
+ allClassMappingsCreated = true;
+ }
+ }
+ }
+
+ return classMappings;
+ }
+
+ private void createAllClassMappings()
+ {
+ InternalRepository repository = (InternalRepository)getStore().getRepository();
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ for (InternalCDOPackageInfo packageInfo : packageRegistry.getPackageInfos())
+ {
+ if (!packageInfo.isSystemPackage())
+ {
+ for (EClassifier eClassifier : packageInfo.getEPackage().getEClassifiers())
+ {
+ if (eClassifier instanceof EClass)
+ {
+ EClass eClass = (EClass)eClassifier;
+ if (isMapped(eClass))
+ {
+ getClassMapping(eClass); // Get or create it
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract boolean isMapped(EClass eClass);
+
+ public ITypeMapping createValueMapping(EStructuralFeature feature)
+ {
+ ITypeMapping.Provider provider = getTypeMappingProvider();
+ return provider.createTypeMapping(this, feature);
+ }
+
+ protected ITypeMapping.Provider getTypeMappingProvider()
+ {
+ return ITypeMapping.Provider.INSTANCE;
+ }
+
+ public final IListMapping createListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ checkArg(feature.isMany(), "Only many-valued features allowed"); //$NON-NLS-1$
+ IListMapping mapping = doCreateListMapping(containingClass, feature);
+ return mapping;
+ }
+
+ public final IListMapping createFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ checkArg(FeatureMapUtil.isFeatureMap(feature), "Only FeatureMaps allowed"); //$NON-NLS-1$
+ IListMapping mapping = doCreateFeatureMapMapping(containingClass, feature);
+ return mapping;
+ }
+
+ public abstract IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature);
+
+ public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java
new file mode 100644
index 0000000000..f5baf6a4c6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java
@@ -0,0 +1,882 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 285270: [DB] Support XSD based models
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lob.CDOBlob;
+import org.eclipse.emf.cdo.common.lob.CDOClob;
+import org.eclipse.emf.cdo.common.lob.CDOLobUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionData;
+import org.eclipse.emf.cdo.etypes.EtypesPackage;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMapping;
+import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMappingFactory;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import org.eclipse.emf.common.util.Enumerator;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EEnumLiteral;
+import org.eclipse.emf.ecore.EFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common
+ * types.
+ *
+ * @author Eike Stepper
+ */
+public class CoreTypeMappings
+{
+ public static final String ID_PREFIX = "org.eclipse.emf.cdo.server.db.CoreTypeMappings";
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMEnum extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Enum",
+ EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ // see Bug 271941
+ return resultSet.getInt(getField().getName());
+ // EEnum type = (EEnum)getFeature().getEType();
+ // int value = resultSet.getInt(column);
+ // return type.getEEnumLiteral(value);
+ }
+
+ @Override
+ protected Object getDefaultValue()
+ {
+ EEnum eenum = (EEnum)getFeature().getEType();
+
+ String defaultValueLiteral = getFeature().getDefaultValueLiteral();
+ if (defaultValueLiteral != null)
+ {
+ EEnumLiteral literal = eenum.getEEnumLiteralByLiteral(defaultValueLiteral);
+ return literal.getValue();
+ }
+
+ Enumerator enumerator = (Enumerator)eenum.getDefaultValue();
+ return enumerator.getValue();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMEnum();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMString extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".StringVarchar", EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".StringLongVarchar", EcorePackage.eINSTANCE.getEString(), DBType.LONGVARCHAR));
+
+ public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".StringClob",
+ EcorePackage.eINSTANCE.getEString(), DBType.CLOB));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getString(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMString();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBlob extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BlobStream", EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BlobStreamLongVarchar", EtypesPackage.eINSTANCE.getBlob(), DBType.LONGVARCHAR));
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ CDOBlob blob = (CDOBlob)value;
+ stmt.setString(index, HexUtil.bytesToHex(blob.getID()) + "-" + blob.getSize());
+ }
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (str == null)
+ {
+ return null;
+ }
+
+ int pos = str.indexOf('-');
+
+ byte[] id = HexUtil.hexToBytes(str.substring(0, pos));
+ long size = Long.parseLong(str.substring(pos + 1));
+ return CDOLobUtil.createBlob(id, size);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBlob();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMClob extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ClobStream", EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ClobStreamLongVarchar", EtypesPackage.eINSTANCE.getClob(), DBType.LONGVARCHAR));
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ CDOClob clob = (CDOClob)value;
+ stmt.setString(index, HexUtil.bytesToHex(clob.getID()) + "-" + clob.getSize());
+ }
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (str == null)
+ {
+ return null;
+ }
+
+ int pos = str.indexOf('-');
+
+ byte[] id = HexUtil.hexToBytes(str.substring(0, pos));
+ long size = Long.parseLong(str.substring(pos + 1));
+ return CDOLobUtil.createClob(id, size);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMClob();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMShort extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Short",
+ EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ShortObject", EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getShort(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMShort();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper <br>
+ */
+ public static class TMObject extends AbstractTypeMapping
+ {
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ CDOID id = idHandler.getCDOID(resultSet, getField().getName());
+
+ if (id == null && getFeature().isUnsettable())
+ {
+ return CDORevisionData.NIL;
+ }
+
+ return id;
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ idHandler.setCDOID(stmt, index, (CDOID)value);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMObject();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMLong extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Long",
+ EcorePackage.eINSTANCE.getELong(), DBType.BIGINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(
+ ID_PREFIX + ".LongObject", EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getLong(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMLong();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMInteger extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Integer",
+ EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".IntegerObject", EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getInt(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMInteger();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMFloat extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Float",
+ EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".FloatObject", EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getFloat(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMFloat();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMDouble extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Double",
+ EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".DoubleObject", EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getDouble(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDouble();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMDate2Timestamp extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Timestamp",
+ EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getTimestamp(getField().getName());
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setTimestamp(index, new Timestamp(((Date)value).getTime()));
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Timestamp();
+ }
+ }
+ }
+
+ /**
+ * @author Heiko Ahlig
+ */
+ public static class TMDate2Date extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Date",
+ EcorePackage.eINSTANCE.getEDate(), DBType.DATE));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getDate(getField().getName(), Calendar.getInstance());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Date();
+ }
+ }
+ }
+
+ /**
+ * @author Heiko Ahlig
+ */
+ public static class TMDate2Time extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Time",
+ EcorePackage.eINSTANCE.getEDate(), DBType.TIME));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getTime(getField().getName(), Calendar.getInstance());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Time();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMCharacter extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Character",
+ EcorePackage.eINSTANCE.getEChar(), DBType.CHAR));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CharacterObject", EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return str.charAt(0);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((Character)value).toString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMCharacter();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMByte extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Byte",
+ EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(
+ ID_PREFIX + ".ByteObject", EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getByte(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMByte();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBytes extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".ByteArray",
+ EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getBytes(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBytes();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBoolean extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Boolean",
+ EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BooleanObject", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN));
+
+ public static final Factory FACTORY_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".Boolean_SMALLINT", EcorePackage.eINSTANCE.getEBoolean(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BooleanObject_SMALLINT", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getBoolean(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBoolean();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMBigInteger extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigInteger",
+ EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BigIntegerLongVarChar", EcorePackage.eINSTANCE.getEBigInteger(), DBType.LONGVARCHAR));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return new BigInteger(val);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((BigInteger)value).toString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBigInteger();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMBigDecimal extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigDecimal",
+ EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BigDecimalLongVarchar", EcorePackage.eINSTANCE.getEBigDecimal(), DBType.LONGVARCHAR));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return new BigDecimal(val);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((BigDecimal)value).toPlainString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBigDecimal();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMCustom extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CustomVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CustomLongVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.LONGVARCHAR));
+
+ public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".CustomClob",
+ EcorePackage.eINSTANCE.getEDataType(), DBType.CLOB));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return val;
+ }
+
+ @Override
+ protected Object getDefaultValue()
+ {
+ Object defaultValue = getFeature().getDefaultValue();
+ if (defaultValue == null)
+ {
+ return null;
+ }
+
+ EFactory factory = getFeature().getEType().getEPackage().getEFactoryInstance();
+ return factory.convertToString((EDataType)getFeature().getEType(), defaultValue);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMCustom();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java
new file mode 100644
index 0000000000..f47075d0d2
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClassifier;
+
+/**
+ * @author Stefan Winkler
+ */
+public class TypeMappingDescriptor implements ITypeMapping.Descriptor
+{
+ private String id;
+
+ private String factoryType;
+
+ private EClassifier eClassifier;
+
+ private DBType dbType;
+
+ public TypeMappingDescriptor(String id, String factoryType, EClassifier eClassifier, DBType dbType)
+ {
+ this.id = id;
+ this.factoryType = factoryType;
+ this.eClassifier = eClassifier;
+ this.dbType = dbType;
+ }
+
+ public String getID()
+ {
+ return id;
+ }
+
+ public String getFactoryType()
+ {
+ return factoryType;
+ }
+
+ public EClassifier getEClassifier()
+ {
+ return eClassifier;
+ }
+
+ public DBType getDBType()
+ {
+ return dbType;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "TypeMappingDescriptor [" + factoryType + "]";
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java
new file mode 100644
index 0000000000..096e2c97a6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java
@@ -0,0 +1,447 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.etypes.EtypesPackage;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil.FactoryTypeParserException;
+import org.eclipse.emf.cdo.server.internal.db.messages.Messages;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.container.ContainerEvent;
+import org.eclipse.net4j.util.container.IContainerDelta;
+import org.eclipse.net4j.util.container.IContainerDelta.Kind;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.event.IEvent;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.factory.IFactory;
+import org.eclipse.net4j.util.factory.IFactoryKey;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EcorePackage;
+
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * An implementation of both the Registry and Provider interfaces for type mappings. This class is a singleton which
+ * keeps itself in sync with the global factory registry. It reads the available factoryTypes for the type mappings
+ * product type and populates indexes which make it easier to determine and look up the correct factories for a needed
+ * type mapping.
+ *
+ * @author Stefan Winkler
+ */
+public class TypeMappingRegistry implements ITypeMapping.Registry, ITypeMapping.Provider
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, TypeMappingRegistry.class);
+
+ /**
+ * Contains a map from model types to db types which represent default mappings. (I.e., if a model element without db
+ * type annotation is encountered, this map is consulted to retrieve the default type mapping. This map is populated
+ * on a come-first basis. The first mapping for a particular {@link EClassifier} is set as default.
+ */
+ private Map<EClassifier, DBType> classifierDefaultMapping;
+
+ /**
+ * The main TypeMapping index. For any known pair of model and db types the {@link ITypeMapping.Descriptor} is
+ * registered here.
+ */
+ private Map<Pair<EClassifier, DBType>, ITypeMapping.Descriptor> typeMappingByTypes;
+
+ /**
+ * ID-based index. Can be used to lookup an {@link ITypeMapping.Descriptor} for a given ID.
+ */
+ private Map<String, ITypeMapping.Descriptor> typeMappingsById;
+
+ /**
+ * A set of all known mapped DBTypes. This is needed for the feature map mappings.
+ */
+ private Set<DBType> defaultFeatureMapDBTypes;
+
+ /**
+ * A populator which is used to keep the registry in sync with the registered factories of the
+ * {@link IManagedContainer}.
+ */
+ private RegistryPopulator populator = new RegistryPopulator();
+
+ public TypeMappingRegistry()
+ {
+ init();
+ }
+
+ public void init()
+ {
+ populator.disconnect();
+
+ defaultFeatureMapDBTypes = new HashSet<DBType>();
+ typeMappingsById = new HashMap<String, ITypeMapping.Descriptor>();
+ typeMappingByTypes = new HashMap<Pair<EClassifier, DBType>, ITypeMapping.Descriptor>();
+ classifierDefaultMapping = new HashMap<EClassifier, DBType>();
+
+ registerCoreTypeMappings();
+ populator.connect();
+ }
+
+ /**
+ * Register builtin type mapping factories
+ */
+ private void registerCoreTypeMappings()
+ {
+ IManagedContainer container = getContainer();
+ container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_SMALLINT);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT_SMALLINT);
+ container.registerFactory(CoreTypeMappings.TMByte.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMByte.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMBytes.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_CLOB);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMDate2Date.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDate2Time.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDate2Timestamp.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDouble.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDouble.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMEnum.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMFloat.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMFloat.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMInteger.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMInteger.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMLong.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMLong.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMShort.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMShort.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_CLOB);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMClob.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMClob.FACTORY_LONG_VARCHAR);
+
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR);
+
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEChar(), DBType.CHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELong(), DBType.BIGINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR);
+
+ classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR); // TODO Should be DBType.BLOB?
+ classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR); // TODO Should be DBType.CLOB?
+ }
+
+ protected IManagedContainer getContainer()
+ {
+ return IPluginContainer.INSTANCE;
+ }
+
+ public void registerTypeMapping(ITypeMapping.Descriptor descriptor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Registering {0}", descriptor);
+ }
+
+ EClassifier eClassifier = descriptor.getEClassifier();
+ DBType dbType = descriptor.getDBType();
+ Pair<EClassifier, DBType> sourceTargetPair = new Pair<EClassifier, DBType>(eClassifier, dbType);
+
+ // currently we do not support more than one typeMapping per source-target type pair
+ if (typeMappingByTypes.containsKey(sourceTargetPair))
+ {
+ OM.LOG.error(Messages.getString("TypeMappingRegistry.4"));
+ return;
+ }
+
+ if (typeMappingsById.containsKey(descriptor.getID()))
+ {
+ OM.LOG.error(MessageFormat.format(Messages.getString("TypeMappingRegistry.5"), descriptor.getID()));
+ return;
+ }
+
+ typeMappingsById.put(descriptor.getID(), descriptor);
+
+ // register first dbType for classifier as default
+ if (!classifierDefaultMapping.containsKey(eClassifier))
+ {
+ classifierDefaultMapping.put(eClassifier, dbType);
+ }
+
+ defaultFeatureMapDBTypes.add(dbType);
+
+ typeMappingByTypes.put(sourceTargetPair, descriptor);
+ }
+
+ public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature)
+ {
+ ITypeMapping typeMapping = null;
+ if (feature instanceof EReference)
+ {
+ IIDHandler idHandler = mappingStrategy.getStore().getIDHandler();
+ typeMapping = idHandler.getObjectTypeMapping();
+ typeMapping.setDBType(idHandler.getDBType());
+ }
+ else
+ {
+ IDBAdapter dbAdapter = mappingStrategy.getStore().getDBAdapter();
+ DBType dbType = getDBType(feature, dbAdapter);
+
+ ITypeMapping.Descriptor descriptor = null;
+
+ String typeMappingID = DBAnnotation.TYPE_MAPPING.getValue(feature);
+ if (typeMappingID != null)
+ {
+ // lookup annotated mapping
+ descriptor = typeMappingsById.get(typeMappingID);
+
+ if (descriptor == null)
+ {
+ OM.LOG.warn(MessageFormat.format(Messages.getString("TypeMappingRegistry.2"), //
+ typeMappingID, feature.toString()));
+ }
+ }
+
+ if (descriptor == null)
+ {
+ // try to find suitable mapping by type
+ descriptor = getMappingByType(feature, dbType);
+ }
+
+ if (descriptor == null)
+ {
+ EClassifier type = getEType(feature);
+ throw new IllegalStateException(MessageFormat.format(Messages.getString("TypeMappingRegistry.1"), feature
+ .getEContainingClass().getName() + "." + feature.getName(),
+ type.getEPackage().getName() + "." + type.getName(), dbType.getKeyword()));
+ }
+
+ IFactory factory = getContainer().getFactory(ITypeMapping.Factory.PRODUCT_GROUP, descriptor.getFactoryType());
+ typeMapping = (ITypeMapping)factory.create(null);
+ typeMapping.setDBType(dbType);
+ }
+
+ typeMapping.setMappingStrategy(mappingStrategy);
+ typeMapping.setFeature(feature);
+ return typeMapping;
+ }
+
+ private EClassifier getEType(EStructuralFeature feature)
+ {
+ EClassifier classifier = feature.getEType();
+ if (classifier instanceof EEnum)
+ {
+ return EcorePackage.eINSTANCE.getEEnum();
+ }
+
+ if (classifier instanceof EClass)
+ {
+ return EcorePackage.eINSTANCE.getEClass();
+ }
+
+ EPackage ePackage = classifier.getEPackage();
+ if (CDOModelUtil.isCorePackage(ePackage))
+ {
+ return classifier;
+ }
+
+ if (CDOModelUtil.isTypesPackage(ePackage))
+ {
+ return classifier;
+ }
+
+ return EcorePackage.eINSTANCE.getEDataType();
+ }
+
+ private DBType getDBType(EStructuralFeature feature, IDBAdapter dbAdapter)
+ {
+ String typeKeyword = DBAnnotation.COLUMN_TYPE.getValue(feature);
+ if (typeKeyword != null)
+ {
+ DBType dbType = DBType.getTypeByKeyword(typeKeyword);
+ if (dbType == null)
+ {
+ throw new IllegalArgumentException("Unsupported columnType (" + typeKeyword + ") annotation of feature "
+ + feature.getName());
+ }
+
+ return dbType;
+ }
+
+ // No annotation present - lookup default DB type.
+ return getDefaultDBType(getEType(feature), dbAdapter);
+ }
+
+ private DBType getDefaultDBType(EClassifier type, IDBAdapter dbAdapter)
+ {
+ DBType result = classifierDefaultMapping.get(type);
+
+ if (result == null)
+ {
+ result = DBType.VARCHAR;
+ }
+
+ // Give the DBAdapter a chance to override the default type, if it's not supported
+ return dbAdapter.adaptType(result);
+ }
+
+ private ITypeMapping.Descriptor getMappingByType(EStructuralFeature feature, DBType dbType)
+ {
+ // First try: lookup specific mapping for the immediate type.
+ ITypeMapping.Descriptor descriptor = typeMappingByTypes.get(new Pair<EClassifier, DBType>(feature.getEType(),
+ dbType));
+
+ if (descriptor == null)
+ {
+ // Second try: lookup general mapping
+ descriptor = typeMappingByTypes.get(new Pair<EClassifier, DBType>(getEType(feature), dbType));
+ if (descriptor == null)
+ {
+ // Lookup failed. Give up
+ return null;
+ }
+ }
+
+ return descriptor;
+ }
+
+ public Collection<DBType> getDefaultFeatureMapDBTypes()
+ {
+ return defaultFeatureMapDBTypes;
+ }
+
+ /**
+ * Keeps the {@link TypeMappingRegistry} in sync with {@link IManagedContainer#getFactoryRegistry()}.
+ *
+ * @author Stefan Winkler
+ */
+ private class RegistryPopulator implements IListener
+ {
+ private IManagedContainer container = getContainer();
+
+ public RegistryPopulator()
+ {
+ }
+
+ /**
+ * Connect to the factory registry.
+ */
+ public void connect()
+ {
+ populateTypeMappingRegistry();
+ container.getFactoryRegistry().addListener(this);
+ }
+
+ public void disconnect()
+ {
+ container.getFactoryRegistry().removeListener(this);
+ }
+
+ private void populateTypeMappingRegistry()
+ {
+ // get available factory types
+ Set<String> factoryTypes = container.getFactoryTypes(ITypeMapping.Factory.PRODUCT_GROUP);
+
+ // parse the descriptor of each factory type
+ for (String factoryType : factoryTypes)
+ {
+ registerFactoryType(factoryType);
+ }
+ }
+
+ private void registerFactoryType(String factoryType)
+ {
+ ITypeMapping.Descriptor desc;
+
+ try
+ {
+ desc = TypeMappingUtil.descriptorFromFactoryType(factoryType);
+ registerTypeMapping(desc);
+ }
+ catch (FactoryTypeParserException ex)
+ {
+ OM.LOG.warn(ex);
+ }
+ }
+
+ public void notifyEvent(IEvent event)
+ {
+ if (event instanceof ContainerEvent<?>)
+ {
+ @SuppressWarnings("unchecked")
+ ContainerEvent<Map.Entry<IFactoryKey, IFactory>> ev = (ContainerEvent<Entry<IFactoryKey, IFactory>>)event;
+
+ for (IContainerDelta<Map.Entry<IFactoryKey, IFactory>> delta : ev.getDeltas())
+ {
+ IFactoryKey key = delta.getElement().getKey();
+ if (key.getProductGroup().equals(ITypeMapping.Factory.PRODUCT_GROUP))
+ {
+ if (delta.getKind() == Kind.ADDED)
+ {
+ String factoryType = delta.getElement().getKey().getType();
+ registerFactoryType(factoryType);
+ }
+ else
+ // delta.getKind() == Kind.REMOVED
+ {
+ // XXX Runtime removal of typeMappingFactories removal of type mappings is currently not supported.
+ OM.LOG.warn(Messages.getString("TypeMappingRegistry.3"));
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java
new file mode 100644
index 0000000000..1bdbaa1a3f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.messages.Messages;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EPackage;
+
+import java.text.MessageFormat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Stefan Winkler
+ */
+public class TypeMappingUtil
+{
+ private static final Pattern FACTORY_DESCRIPTOR_PATTERN = Pattern.compile("(.+);(.+)#(.+)->(.+)");
+
+ /**
+ * Utility class - no instantiation.
+ */
+ private TypeMappingUtil()
+ {
+ }
+
+ public static ITypeMapping.Descriptor createDescriptor(String id, EClassifier eClassifier, DBType dbType)
+ {
+ String factoryType = createFactoryType(id, eClassifier, dbType);
+ return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType);
+ }
+
+ public static String createFactoryType(String id, EClassifier eClassifier, DBType dbType)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ // id
+ builder.append(id);
+ builder.append(";");
+
+ // classifier
+ builder.append(eClassifier.getEPackage().getNsURI());
+ builder.append("#");
+ builder.append(eClassifier.getName());
+ builder.append("->");
+
+ // dbtype
+ builder.append(dbType.getKeyword());
+
+ return builder.toString();
+ }
+
+ public static ITypeMapping.Descriptor descriptorFromFactoryType(String factoryType) throws FactoryTypeParserException
+ {
+ Matcher matcher = FACTORY_DESCRIPTOR_PATTERN.matcher(factoryType);
+
+ if (!matcher.matches())
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.1"),
+ factoryType));
+ }
+
+ String id = matcher.group(1);
+ String packageUri = matcher.group(2);
+ String classifierName = matcher.group(3);
+ String typeKeyword = matcher.group(4);
+
+ EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(packageUri);
+ if (ePackage == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.2"),
+ packageUri, factoryType));
+ }
+
+ EClassifier eClassifier = ePackage.getEClassifier(classifierName);
+ if (eClassifier == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.3"),
+ classifierName, factoryType));
+ }
+
+ DBType dbType = DBType.getTypeByKeyword(typeKeyword);
+ if (dbType == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.4"),
+ dbType, factoryType));
+ }
+
+ return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType);
+ }
+
+ public static class FactoryTypeParserException extends Exception
+ {
+ private static final long serialVersionUID = 1L;
+
+ public FactoryTypeParserException(String desc)
+ {
+ super(desc);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
new file mode 100644
index 0000000000..83493b4896
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
@@ -0,0 +1,588 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
+ * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMap;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public abstract class AbstractFeatureMapTableMapping extends BasicAbstractListTableMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractFeatureMapTableMapping.class);
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The tags mapped to column names
+ */
+ private HashMap<CDOID, String> tagMap;
+
+ /**
+ * Column name Set
+ */
+ private List<String> columnNames;
+
+ /**
+ * The type mappings for the value fields.
+ */
+ private Map<CDOID, ITypeMapping> typeMappings;
+
+ // --------- SQL strings - see initSQLStrings() -----------------
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ protected String sqlInsert;
+
+ private List<DBType> dbTypes;
+
+ public AbstractFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initDBTypes();
+ initTable();
+ initSQLStrings();
+ }
+
+ private void initDBTypes()
+ {
+ // TODO add annotation processing here ...
+ ITypeMapping.Registry registry = getTypeMappingRegistry();
+ dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes());
+ }
+
+ protected ITypeMapping.Registry getTypeMappingRegistry()
+ {
+ return ITypeMapping.Registry.INSTANCE;
+ }
+
+ private void initTable()
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
+ table = store.getDBSchema().addTable(tableName);
+
+ // add fields for keys (cdo_id, version, feature_id)
+ FieldInfo[] fields = getKeyFields();
+ IDBField[] dbFields = new IDBField[fields.length];
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType());
+ }
+
+ // add field for list index
+ IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER);
+
+ // add field for FeatureMap tag (MetaID for Feature in CDO registry)
+ IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType());
+
+ tagMap = new HashMap<CDOID, String>();
+ typeMappings = new HashMap<CDOID, ITypeMapping>();
+ columnNames = new ArrayList<String>();
+
+ // create columns for all DBTypes
+ for (DBType type : getDBTypes())
+ {
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name();
+ table.addField(column, type);
+ columnNames.add(column);
+ }
+
+ table.addIndex(Type.NON_UNIQUE, dbFields);
+ table.addIndex(Type.NON_UNIQUE, idxField);
+ table.addIndex(Type.NON_UNIQUE, tagField);
+ }
+
+ protected abstract FieldInfo[] getKeyFields();
+
+ protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException;
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ private void initSQLStrings()
+ {
+ String tableName = getTable().getName();
+ FieldInfo[] fields = getKeyFields();
+
+ // ---------------- SELECT to read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT ");
+
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(", ");
+
+ Iterator<String> iter = columnNames.iterator();
+ while (iter.hasNext())
+ {
+ builder.append(iter.next());
+ if (iter.hasNext())
+ {
+ builder.append(", ");
+ }
+ }
+
+ builder.append(" FROM ");
+ builder.append(tableName);
+ builder.append(" WHERE ");
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ if (i + 1 < fields.length)
+ {
+ // more to come
+ builder.append("=? AND ");
+ }
+ else
+ {
+ // last one
+ builder.append("=? ");
+ }
+ }
+
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$
+
+ // INSERT with dynamic field name
+ // TODO: Better: universal INSERT-Statement, because of stmt caching!
+
+ // ----------------- INSERT - prefix -----------------
+ builder = new StringBuilder("INSERT INTO ");
+ builder.append(tableName);
+ builder.append(" ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ builder.append(", "); //$NON-NLS-1$
+ }
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ builder.append(columnNames.get(i));
+ builder.append(", "); //$NON-NLS-1$
+ }
+
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(") VALUES ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length + columnNames.size(); i++)
+ {
+ builder.append("?, ");
+ }
+
+ builder.append("?, ?)");
+ sqlInsert = builder.toString();
+ }
+
+ protected List<DBType> getDBTypes()
+ {
+ return dbTypes;
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final List<String> getColumnNames()
+ {
+ return columnNames;
+ }
+
+ protected final Map<CDOID, ITypeMapping> getTypeMappings()
+ {
+ return typeMappings;
+ }
+
+ protected final Map<CDOID, String> getTagMap()
+ {
+ return tagMap;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ MoveableList<Object> list = revision.getList(getFeature());
+ if (listChunk == 0 || list.size() == 0)
+ {
+ // nothing to read take shortcut
+ return;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature()
+ .getName(), revision.getID(), revision.getVersion());
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+ setKeyFields(stmt, revision);
+
+ if (listChunk != CDORevision.UNCHUNKED)
+ {
+ stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
+ }
+
+ resultSet = stmt.executeQuery();
+ int currentIndex = 0;
+
+ while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
+ {
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", list.size(), value);
+ }
+
+ list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(),
+ getFeature().getName(), revision.getID(), revision.getVersion());
+ }
+ }
+
+ private void addFeature(CDOID tag)
+ {
+ EStructuralFeature modelFeature = getFeatureByTag(tag);
+
+ ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature);
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType();
+
+ tagMap.put(tag, column);
+ typeMapping.setDBField(table, column);
+ typeMappings.put(tag, typeMapping);
+ }
+
+ public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(),
+ getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
+ if (where != null)
+ {
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(where);
+ }
+
+ builder.append(sqlOrderByIndex);
+
+ String sql = builder.toString();
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
+ setKeyFields(stmt, chunkReader.getRevision());
+
+ resultSet = stmt.executeQuery();
+
+ Chunk chunk = null;
+ int chunkSize = 0;
+ int chunkIndex = 0;
+ int indexInChunk = 0;
+
+ while (resultSet.next())
+ {
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+
+ if (chunk == null)
+ {
+ chunk = chunks.get(chunkIndex++);
+ chunkSize = chunk.size();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(),
+ chunkSize);
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value);
+ }
+
+ chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ if (indexInChunk == chunkSize)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Chunk finished");
+ }
+
+ chunk = null;
+ indexInChunk = 0;
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(),
+ getFeature(), chunkReader.getRevision());
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ CDOList values = revision.getList(getFeature());
+
+ int idx = 0;
+ for (Object element : values)
+ {
+ writeValue(accessor, revision, idx++, element);
+ }
+ }
+
+ protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER
+ .format(
+ "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$
+ }
+
+ try
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ CDOID tag = getTagByFeature(entryFeature, revision.getTimeStamp());
+ String columnName = getColumnName(tag);
+
+ stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH);
+ setKeyFields(stmt, revision);
+ int column = getKeyFields().length + 1;
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ if (columnNames.get(i).equals(columnName))
+ {
+ getTypeMapping(tag).setValue(stmt, column++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(column++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ stmt.setInt(column++, idx);
+ idHandler.setCDOID(stmt, column++, tag);
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ /**
+ * Get column name (lazy)
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the column name where the values are stored
+ */
+ protected String getColumnName(CDOID tag)
+ {
+ String column = tagMap.get(tag);
+ if (column == null)
+ {
+ addFeature(tag);
+ column = tagMap.get(tag);
+ }
+
+ return column;
+ }
+
+ /**
+ * Get type mapping (lazy)
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the corresponding type mapping
+ */
+ protected ITypeMapping getTypeMapping(CDOID tag)
+ {
+ ITypeMapping typeMapping = typeMappings.get(tag);
+ if (typeMapping == null)
+ {
+ addFeature(tag);
+ typeMapping = typeMappings.get(tag);
+ }
+
+ return typeMapping;
+ }
+
+ /**
+ * @param metaID
+ * @return the column name where the values are stored
+ */
+ private EStructuralFeature getFeatureByTag(CDOID tag)
+ {
+ return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag);
+ }
+
+ /**
+ * @param feature
+ * The EStructuralFeature
+ * @return The feature's MetaID in CDO
+ */
+ protected CDOID getTagByFeature(EStructuralFeature feature, long timeStamp)
+ {
+ return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timeStamp);
+ }
+
+ /**
+ * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ...
+ *
+ * @author Stefan Winkler
+ */
+ protected static class FieldInfo
+ {
+ private String name;
+
+ private DBType dbType;
+
+ public FieldInfo(String name, DBType dbType)
+ {
+ this.name = name;
+ this.dbType = dbType;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public DBType getDbType()
+ {
+ return dbType;
+ }
+ }
+
+ public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
+ QueryXRefsContext context, String idString)
+ {
+ /*
+ * must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported
+ * here)
+ */
+ throw new ImplementationError("Should never be called!");
+ }
+
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java
new file mode 100644
index 0000000000..bb500d3e4a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java
@@ -0,0 +1,870 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchManager;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
+import org.eclipse.emf.cdo.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class AbstractHorizontalClassMapping implements IClassMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class);
+
+ private EClass eClass;
+
+ private IDBTable table;
+
+ private AbstractHorizontalMappingStrategy mappingStrategy;
+
+ private List<ITypeMapping> valueMappings;
+
+ private List<IListMapping> listMappings;
+
+ private Map<EStructuralFeature, String> listSizeFields;
+
+ private Map<EStructuralFeature, String> unsettableFields;
+
+ private String sqlSelectForHandle;
+
+ private String sqlSelectForChangeSet;
+
+ public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
+ {
+ this.mappingStrategy = mappingStrategy;
+ this.eClass = eClass;
+
+ initTable();
+ initFeatures();
+ initSQLStrings();
+ }
+
+ private void initTable()
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ DBType idType = store.getIDHandler().getDBType();
+
+ String name = getMappingStrategy().getTableName(eClass);
+ table = store.getDBSchema().addTable(name);
+
+ IDBField idField = table.addField(CDODBSchema.ATTRIBUTES_ID, idType, true);
+ IDBField versionField = table.addField(CDODBSchema.ATTRIBUTES_VERSION, DBType.INTEGER, true);
+
+ IDBField branchField = addBranchingField(table);
+
+ table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT, true);
+ IDBField revisedField = table.addField(CDODBSchema.ATTRIBUTES_REVISED, DBType.BIGINT, true);
+ table.addField(CDODBSchema.ATTRIBUTES_RESOURCE, idType, true);
+ table.addField(CDODBSchema.ATTRIBUTES_CONTAINER, idType, true);
+ table.addField(CDODBSchema.ATTRIBUTES_FEATURE, DBType.INTEGER, true);
+
+ if (branchField != null)
+ {
+ table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField, branchField);
+ }
+ else
+ {
+ table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField);
+ }
+
+ table.addIndex(IDBIndex.Type.NON_UNIQUE, idField, revisedField);
+ }
+
+ protected IDBField addBranchingField(IDBTable table)
+ {
+ return null;
+ }
+
+ private void initFeatures()
+ {
+ EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass);
+
+ if (features == null)
+ {
+ valueMappings = Collections.emptyList();
+ listMappings = Collections.emptyList();
+ }
+ else
+ {
+ valueMappings = createValueMappings(features);
+ listMappings = createListMappings(features);
+ }
+ }
+
+ private void initSQLStrings()
+ {
+ // ----------- Select all revisions (for handleRevisions) ---
+ StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ sqlSelectForHandle = builder.toString();
+
+ // ----------- Select all revisions (for readChangeSet) ---
+ builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ sqlSelectForChangeSet = builder.toString();
+ }
+
+ private List<ITypeMapping> createValueMappings(EStructuralFeature[] features)
+ {
+ List<ITypeMapping> mappings = new ArrayList<ITypeMapping>();
+ for (EStructuralFeature feature : features)
+ {
+ if (!feature.isMany())
+ {
+ ITypeMapping mapping = mappingStrategy.createValueMapping(feature);
+ mapping.createDBField(getTable());
+ mappings.add(mapping);
+
+ if (feature.isUnsettable())
+ {
+ String fieldName = mappingStrategy.getUnsettableFieldName(feature);
+ if (unsettableFields == null)
+ {
+ unsettableFields = new LinkedHashMap<EStructuralFeature, String>();
+ }
+
+ unsettableFields.put(feature, fieldName);
+ }
+ }
+ }
+
+ // add unsettable fields to end of table
+ if (unsettableFields != null)
+ {
+ for (String fieldName : unsettableFields.values())
+ {
+ table.addField(fieldName, DBType.BOOLEAN, 1);
+ }
+ }
+
+ return mappings;
+ }
+
+ private List<IListMapping> createListMappings(EStructuralFeature[] features)
+ {
+ List<IListMapping> listMappings = new ArrayList<IListMapping>();
+ for (EStructuralFeature feature : features)
+ {
+ if (feature.isMany())
+ {
+ IListMapping mapping = null;
+ if (FeatureMapUtil.isFeatureMap(feature))
+ {
+ mapping = mappingStrategy.createFeatureMapMapping(eClass, feature);
+ }
+ else
+ {
+ mapping = mappingStrategy.createListMapping(eClass, feature);
+ }
+
+ listMappings.add(mapping);
+
+ // add field for list sizes
+ createListSizeField(feature);
+ }
+ }
+
+ return listMappings;
+ }
+
+ /**
+ * Create an integer field in the attribute tabel for the list size of the associated list mapping.
+ */
+ private void createListSizeField(EStructuralFeature feature)
+ {
+ if (listSizeFields == null)
+ {
+ listSizeFields = new LinkedHashMap<EStructuralFeature, String>();
+ }
+
+ String fieldName = mappingStrategy.getFieldName(feature);
+ table.addField(fieldName, DBType.INTEGER);
+
+ listSizeFields.put(feature, fieldName);
+ }
+
+ /**
+ * Read the revision's values from the DB.
+ *
+ * @return <code>true</code> if the revision has been read successfully.<br>
+ * <code>false</code> if the revision does not exist in the DB.
+ */
+ protected final boolean readValuesFromStatement(PreparedStatement stmt, InternalCDORevision revision,
+ IDBStoreAccessor accessor)
+ {
+ ResultSet resultSet = null;
+
+ try
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Executing Query: {0}", stmt.toString()); //$NON-NLS-1$
+ }
+
+ stmt.setMaxRows(1); // Optimization: only 1 row
+
+ resultSet = stmt.executeQuery();
+ if (!resultSet.next())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Resultset was empty"); //$NON-NLS-1$
+ }
+
+ return false;
+ }
+
+ revision.setVersion(resultSet.getInt(CDODBSchema.ATTRIBUTES_VERSION));
+
+ long timeStamp = resultSet.getLong(CDODBSchema.ATTRIBUTES_CREATED);
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp);
+
+ revision.setBranchPoint(branchPoint);
+ revision.setRevised(resultSet.getLong(CDODBSchema.ATTRIBUTES_REVISED));
+ revision.setResourceID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_RESOURCE));
+ revision.setContainerID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_CONTAINER));
+ revision.setContainingFeatureID(resultSet.getInt(CDODBSchema.ATTRIBUTES_FEATURE));
+
+ for (ITypeMapping mapping : valueMappings)
+ {
+ EStructuralFeature feature = mapping.getFeature();
+ if (feature.isUnsettable())
+ {
+ if (!resultSet.getBoolean(unsettableFields.get(feature)))
+ {
+ // isSet==false -- setValue: null
+ revision.setValue(feature, null);
+ continue;
+ }
+ }
+
+ mapping.readValueToRevision(resultSet, revision);
+ }
+
+ if (listSizeFields != null)
+ {
+ for (Map.Entry<EStructuralFeature, String> listSizeEntry : listSizeFields.entrySet())
+ {
+ EStructuralFeature feature = listSizeEntry.getKey();
+ String fieldName = listSizeEntry.getValue();
+ int size = resultSet.getInt(fieldName);
+
+ // ensure the listSize (TODO: remove assertion)
+ CDOList list = revision.getList(feature, size);
+
+ for (int i = 0; i < size; i++)
+ {
+ list.add(InternalCDOList.UNINITIALIZED);
+ }
+
+ if (list.size() != size)
+ {
+ Assert.isTrue(false);
+ }
+ }
+ }
+
+ return true;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ }
+ }
+
+ protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ for (IListMapping listMapping : listMappings)
+ {
+ listMapping.readValues(accessor, revision, listChunk);
+ }
+ }
+
+ protected final IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public final EClass getEClass()
+ {
+ return eClass;
+ }
+
+ protected final Map<EStructuralFeature, String> getUnsettableFields()
+ {
+ return unsettableFields;
+ }
+
+ protected final Map<EStructuralFeature, String> getListSizeFields()
+ {
+ return listSizeFields;
+ }
+
+ public final List<ITypeMapping> getValueMappings()
+ {
+ return valueMappings;
+ }
+
+ public final ITypeMapping getValueMapping(EStructuralFeature feature)
+ {
+ for (ITypeMapping mapping : valueMappings)
+ {
+ if (mapping.getFeature() == feature)
+ {
+ return mapping;
+ }
+ }
+
+ return null;
+ }
+
+ public final List<IListMapping> getListMappings()
+ {
+ return listMappings;
+ }
+
+ public final IListMapping getListMapping(EStructuralFeature feature)
+ {
+ for (IListMapping mapping : listMappings)
+ {
+ if (mapping.getFeature() == feature)
+ {
+ return mapping;
+ }
+ }
+
+ throw new IllegalArgumentException("List mapping for feature " + feature + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ public List<IDBTable> getDBTables()
+ {
+ List<IDBTable> tables = new ArrayList<IDBTable>();
+ tables.add(table);
+
+ for (IListMapping listMapping : listMappings)
+ {
+ tables.addAll(listMapping.getDBTables());
+ }
+
+ return tables;
+ }
+
+ protected void checkDuplicateResources(IDBStoreAccessor accessor, CDORevision revision) throws IllegalStateException
+ {
+ CDOID folderID = (CDOID)revision.data().getContainerID();
+ String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0);
+ CDOID existingID = accessor.readResourceID(folderID, name, revision.getBranch().getHead());
+ if (existingID != null && !existingID.equals(revision.getID()))
+ {
+ throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ protected void writeLists(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ for (IListMapping listMapping : listMappings)
+ {
+ listMapping.writeValues(accessor, revision);
+ }
+ }
+
+ public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise,
+ OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin(10);
+
+ try
+ {
+ try
+ {
+ async = monitor.forkAsync();
+ CDOID id = revision.getID();
+ if (mapType)
+ {
+ long timeStamp = revision.getTimeStamp();
+ mappingStrategy.putObjectType(accessor, timeStamp, id, eClass);
+ }
+ else if (revise)
+ {
+ long revised = revision.getTimeStamp() - 1;
+ reviseOldRevision(accessor, id, revision.getBranch(), revised);
+ for (IListMapping mapping : getListMappings())
+ {
+ mapping.objectDetached(accessor, id, revised);
+ }
+ }
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+
+ try
+ {
+ async = monitor.forkAsync();
+ if (revision.isResourceFolder() || revision.isResource())
+ {
+ checkDuplicateResources(accessor, revision);
+ }
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+
+ try
+ {
+ // Write attribute table always (even without modeled attributes!)
+ async = monitor.forkAsync();
+ writeValues(accessor, revision);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+
+ try
+ {
+ // Write list tables only if they exist
+ if (listMappings != null)
+ {
+ async = monitor.forkAsync(7);
+ writeLists(accessor, revision);
+ }
+ else
+ {
+ monitor.worked(7);
+ }
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler)
+ {
+ // branch parameter is ignored, because either it is null or main branch.
+ // this does not make any difference for non-branching store.
+ // see #handleRevisions() implementation in HorizontalBranchingClassMapping
+ // for branch handling.
+
+ IRepository repository = accessor.getStore().getRepository();
+ CDORevisionManager revisionManager = repository.getRevisionManager();
+ CDOBranchManager branchManager = repository.getBranchManager();
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ // TODO: test for timeStamp == INVALID_TIME and encode revision.isValid() as WHERE instead of fetching all revisions
+ // in order to increase performance
+
+ StringBuilder builder = new StringBuilder(sqlSelectForHandle);
+
+ int timeParameters = 0;
+ if (timeStamp != CDOBranchPoint.INVALID_DATE)
+ {
+ if (exactTime)
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append("=?"); //$NON-NLS-1$
+ timeParameters = 1;
+ }
+ }
+ else
+ {
+ builder.append(" WHERE "); //$NON-NLS-1$
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(">=?"); //$NON-NLS-1$
+ builder.append(" AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("<=? OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
+ builder.append(")"); //$NON-NLS-1$
+ timeParameters = 2;
+ }
+ else
+ {
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
+ }
+ }
+ }
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW);
+ for (int i = 0; i < timeParameters; i++)
+ {
+ stmt.setLong(i + 1, timeStamp);
+ }
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ int version = resultSet.getInt(2);
+
+ if (version >= CDOBranchVersion.FIRST_VERSION)
+ {
+ InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchManager
+ .getMainBranch().getVersion(version), CDORevision.UNCHUNKED, true);
+
+ if (!handler.handleRevision(revision))
+ {
+ break;
+ }
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments)
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectForChangeSet);
+ boolean isFirst = true;
+
+ for (int i = 0; i < segments.length; i++)
+ {
+ if (isFirst)
+ {
+ isFirst = false;
+ }
+ else
+ {
+ builder.append(" OR "); //$NON-NLS-1$
+ }
+
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(">=?"); //$NON-NLS-1$
+ builder.append(" AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("<=? OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_REVISED);
+ builder.append("="); //$NON-NLS-1$
+ builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
+ builder.append(")"); //$NON-NLS-1$
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ Set<CDOID> result = new HashSet<CDOID>();
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW);
+ int column = 1;
+ for (CDOChangeSetSegment segment : segments)
+ {
+ stmt.setLong(column++, segment.getTimeStamp());
+ stmt.setLong(column++, segment.getEndTime());
+ }
+
+ resultSet = stmt.executeQuery();
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ result.add(id);
+ }
+
+ return result;
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin(1 + listMappings.size());
+
+ try
+ {
+ if (version >= CDOBranchVersion.FIRST_VERSION)
+ {
+ reviseOldRevision(accessor, id, branch, timeStamp - 1);
+ }
+
+ detachAttributes(accessor, id, version + 1, branch, timeStamp, monitor.fork());
+
+ // notify list mappings so they can clean up
+ for (IListMapping mapping : getListMappings())
+ {
+ try
+ {
+ async = monitor.forkAsync();
+ mapping.objectDetached(accessor, id, timeStamp);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString)
+ {
+ String tableName = getTable().getName();
+ EClass eClass = getEClass();
+ List<EReference> refs = context.getSourceCandidates().get(eClass);
+ List<EReference> scalarRefs = new ArrayList<EReference>();
+
+ for (EReference ref : refs)
+ {
+ if (ref.isMany())
+ {
+ IListMapping listMapping = getListMapping(ref);
+ String where = getListXRefsWhere(context);
+
+ boolean more = listMapping.queryXRefs(accessor, tableName, where, context, idString);
+ if (!more)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ scalarRefs.add(ref);
+ }
+ }
+
+ if (!scalarRefs.isEmpty())
+ {
+ boolean more = queryScalarXRefs(accessor, scalarRefs, context, idString);
+ if (!more)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected final boolean queryScalarXRefs(IDBStoreAccessor accessor, List<EReference> scalarRefs,
+ QueryXRefsContext context, String idString)
+ {
+ String tableName = getTable().getName();
+ String where = getListXRefsWhere(context);
+
+ for (EReference ref : scalarRefs)
+ {
+ ITypeMapping valueMapping = getValueMapping(ref);
+ String valueField = valueMapping.getField().getName();
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT ");
+ builder.append(CDODBSchema.ATTRIBUTES_ID);
+ builder.append(", ");
+ builder.append(valueField);
+ builder.append(" FROM ");
+ builder.append(tableName);
+ builder.append(" WHERE ");
+ builder.append(CDODBSchema.ATTRIBUTES_VERSION);
+ builder.append(">0 AND ");
+ builder.append(where);
+ builder.append(" AND ");
+ builder.append(valueField);
+ builder.append(" IN ");
+ builder.append(idString);
+ String sql = builder.toString();
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ ResultSet resultSet = null;
+ Statement stmt = null;
+
+ try
+ {
+ stmt = accessor.getConnection().createStatement();
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Query XRefs (attributes): {0}", sql);
+ }
+
+ resultSet = stmt.executeQuery(sql);
+ while (resultSet.next())
+ {
+ CDOID sourceID = idHandler.getCDOID(resultSet, 1);
+ CDOID targetID = idHandler.getCDOID(resultSet, 2);
+
+ boolean more = context.addXRef(targetID, sourceID, ref, 0);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" add XRef to context: src={0}, tgt={1}, idx=0", sourceID, targetID);
+ }
+
+ if (!more)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" result limit reached. Ignoring further results.");
+ }
+
+ return false;
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmt);
+ }
+ }
+
+ return true;
+ }
+
+ protected abstract String getListXRefsWhere(QueryXRefsContext context);
+
+ protected abstract void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch,
+ long timeStamp, OMMonitor fork);
+
+ protected abstract void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp);
+
+ protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java
new file mode 100644
index 0000000000..7a9cd33b45
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java
@@ -0,0 +1,480 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.eresource.CDOResourceNode;
+import org.eclipse.emf.cdo.eresource.EresourcePackage;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.AbstractMappingStrategy;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * * This abstract base class refines {@link AbstractMappingStrategy} by implementing aspects common to horizontal
+ * mapping strategies -- namely:
+ * <ul>
+ * <li>object type cache (table cdo_objects)
+ * <li>resource query handling
+ * </ul>
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class AbstractHorizontalMappingStrategy extends AbstractMappingStrategy
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalMappingStrategy.class);
+
+ /**
+ * The associated object type mapper.
+ */
+ private IObjectTypeMapper objectTypeMapper;
+
+ public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id)
+ {
+ return objectTypeMapper.getObjectType(accessor, id);
+ }
+
+ public void putObjectType(IDBStoreAccessor accessor, long timeStamp, CDOID id, EClass type)
+ {
+ objectTypeMapper.putObjectType(accessor, timeStamp, id, type);
+ }
+
+ public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection)
+ {
+ IDBStore store = getStore();
+ if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
+ {
+ IIDHandler idHandler = store.getIDHandler();
+
+ CDOID minLocalID = getMinLocalID(connection);
+ idHandler.setNextLocalObjectID(minLocalID);
+
+ CDOID maxID = objectTypeMapper.getMaxID(connection, idHandler);
+ idHandler.setLastObjectID(maxID);
+ }
+ }
+
+ public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context)
+ {
+ // only support timestamp in audit mode
+ if (context.getTimeStamp() != CDORevision.UNSPECIFIED_DATE && !hasAuditSupport())
+ {
+ throw new IllegalArgumentException("Mapping Strategy does not support audits"); //$NON-NLS-1$
+ }
+
+ EresourcePackage resourcesPackage = EresourcePackage.eINSTANCE;
+
+ // first query folders
+ IClassMapping resourceFolder = getClassMapping(resourcesPackage.getCDOResourceFolder());
+ boolean shallContinue = queryResources(accessor, resourceFolder, context);
+
+ // not enough results? -> query resources
+ if (shallContinue)
+ {
+ IClassMapping resource = getClassMapping(resourcesPackage.getCDOResource());
+ queryResources(accessor, resource, context);
+ }
+ }
+
+ public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context)
+ {
+ IIDHandler idHandler = getStore().getIDHandler();
+ StringBuilder builder = null;
+
+ // create a string containing "(id1,id2,...)"
+ // NOTE: this might not scale infinitely, because of dbms-dependent
+ // max size for SQL strings. But for now, it's the easiest way...
+ for (CDOID targetID : context.getTargetObjects().keySet())
+ {
+ // NOTE: currently no support for external references!
+ if (builder == null)
+ {
+ builder = new StringBuilder("(");
+ }
+ else
+ {
+ builder.append(",");
+ }
+
+ idHandler.appendCDOID(builder, targetID);
+ }
+
+ builder.append(")");
+ String idString = builder.toString();
+
+ for (EClass eClass : context.getSourceCandidates().keySet())
+ {
+ IClassMapping classMapping = getClassMapping(eClass);
+ boolean more = classMapping.queryXRefs(accessor, context, idString);
+ if (!more)
+ {
+ // cancel query (max results reached or user canceled)
+ return;
+ }
+ }
+ }
+
+ public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int fromBranchID, int toBranchID,
+ long fromCommitTime, long toCommitTime) throws IOException
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" WHERE a_t."); //$NON-NLS-1$
+ builder.append(CDODBSchema.ATTRIBUTES_CREATED);
+ builder.append(" BETWEEN "); //$NON-NLS-1$
+ builder.append(fromCommitTime);
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(toCommitTime);
+
+ String attrSuffix = builder.toString();
+ Connection connection = accessor.getConnection();
+
+ Collection<IClassMapping> classMappings = getClassMappings(true).values();
+ out.writeInt(classMappings.size());
+
+ for (IClassMapping classMapping : classMappings)
+ {
+ EClass eClass = classMapping.getEClass();
+ out.writeCDOClassifierRef(eClass);
+
+ IDBTable table = classMapping.getDBTables().get(0);
+ DBUtil.serializeTable(out, connection, table, "a_t", attrSuffix);
+
+ for (IListMapping listMapping : classMapping.getListMappings())
+ {
+ rawExportList(out, connection, listMapping, table, attrSuffix);
+ }
+ }
+
+ objectTypeMapper.rawExport(connection, out, fromCommitTime, toCommitTime);
+ }
+
+ protected void rawExportList(CDODataOutput out, Connection connection, IListMapping listMapping, IDBTable attrTable,
+ String attrSuffix) throws IOException
+ {
+ for (IDBTable table : listMapping.getDBTables())
+ {
+ String listSuffix = ", " + attrTable + " a_t" + attrSuffix;
+ String listJoin = getListJoin("a_t", "l_t");
+ if (listJoin != null)
+ {
+ listSuffix += listJoin;
+ }
+
+ DBUtil.serializeTable(out, connection, table, "l_t", listSuffix);
+ }
+ }
+
+ public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ int size = in.readInt();
+ if (size == 0)
+ {
+ return;
+ }
+
+ int objectTypeMapperWork = 10;
+ monitor.begin(3 * size + objectTypeMapperWork);
+
+ try
+ {
+ Connection connection = accessor.getConnection();
+ for (int i = 0; i < size; i++)
+ {
+ EClass eClass = (EClass)in.readCDOClassifierRefAndResolve();
+ IClassMapping classMapping = getClassMapping(eClass);
+
+ IDBTable table = classMapping.getDBTables().get(0);
+ DBUtil.deserializeTable(in, connection, table, monitor.fork());
+ rawImportReviseOldRevisions(connection, table, monitor.fork());
+ rawImportUnreviseNewRevisions(connection, table, fromCommitTime, toCommitTime, monitor.fork());
+
+ List<IListMapping> listMappings = classMapping.getListMappings();
+ int listSize = listMappings.size();
+ if (listSize == 0)
+ {
+ monitor.worked();
+ }
+ else
+ {
+ OMMonitor listMonitor = monitor.fork();
+ listMonitor.begin(listSize);
+
+ try
+ {
+ for (IListMapping listMapping : listMappings)
+ {
+ rawImportList(in, connection, listMapping, listMonitor.fork());
+ }
+ }
+ finally
+ {
+ listMonitor.done();
+ }
+ }
+ }
+
+ objectTypeMapper.rawImport(connection, in, monitor.fork(objectTypeMapperWork));
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ protected void rawImportUnreviseNewRevisions(Connection connection, IDBTable table, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException("Must be overridden");
+ }
+
+ protected void rawImportReviseOldRevisions(Connection connection, IDBTable table, OMMonitor monitor)
+ {
+ throw new UnsupportedOperationException("Must be overridden");
+ }
+
+ protected void rawImportList(CDODataInput in, Connection connection, IListMapping listMapping, OMMonitor monitor)
+ throws IOException
+ {
+ Collection<IDBTable> tables = listMapping.getDBTables();
+ int size = tables.size();
+ if (size == 0)
+ {
+ return;
+ }
+
+ monitor.begin(size);
+
+ try
+ {
+ for (IDBTable table : tables)
+ {
+ DBUtil.deserializeTable(in, connection, table, monitor.fork());
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public String getListJoin(String attrTable, String listTable)
+ {
+ return " AND " + attrTable + "." + CDODBSchema.ATTRIBUTES_ID + "=" + listTable + "." + CDODBSchema.LIST_REVISION_ID;
+ }
+
+ @Override
+ protected boolean isMapped(EClass eClass)
+ {
+ return !eClass.isAbstract() && !eClass.isInterface();
+ }
+
+ @Override
+ protected Collection<EClass> getClassesWithObjectInfo()
+ {
+ return getClassMappings().keySet();
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ super.doActivate();
+ if (objectTypeMapper == null)
+ {
+ objectTypeMapper = createObjectTypeMapper();
+ }
+
+ LifecycleUtil.activate(objectTypeMapper);
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ LifecycleUtil.deactivate(objectTypeMapper);
+ super.doDeactivate();
+ }
+
+ private IObjectTypeMapper createObjectTypeMapper()
+ {
+ ObjectTypeTable table = new ObjectTypeTable();
+ table.setMappingStrategy(this);
+
+ int cacheSize = getObjectTypeCacheSize();
+ if (cacheSize == 0)
+ {
+ return table;
+ }
+
+ ObjectTypeCache cache = new ObjectTypeCache(cacheSize);
+ cache.setMappingStrategy(this);
+ cache.setDelegate(table);
+ return cache;
+ }
+
+ private int getObjectTypeCacheSize()
+ {
+ int objectTypeCacheSize = ObjectTypeCache.DEFAULT_CACHE_CAPACITY;
+
+ Object value = getProperties().get(PROP_OBJECT_TYPE_CACHE_SIZE);
+ if (value != null)
+ {
+ try
+ {
+ int intValue = Integer.parseInt((String)value);
+ objectTypeCacheSize = intValue;
+ }
+ catch (NumberFormatException e)
+ {
+ OM.LOG.warn("Malformed configuration option for object type cache size. Using default.");
+ }
+ }
+
+ return objectTypeCacheSize;
+ }
+
+ /**
+ * This is an intermediate implementation. It should be changed after classmappings support a general way to implement
+ * queries ...
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param classMapping
+ * the class mapping of a class instanceof {@link CDOResourceNode} which should be queried.
+ * @param context
+ * the query context containing the parameters and the result.
+ * @return <code>true</code> if result context is not yet full and query should continue false, if result context is
+ * full and query should stop.
+ */
+ private boolean queryResources(IDBStoreAccessor accessor, IClassMapping classMapping, QueryResourcesContext context)
+ {
+ IIDHandler idHandler = getStore().getIDHandler();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ CDOID folderID = context.getFolderID();
+ String name = context.getName();
+ boolean exactMatch = context.exactMatch();
+
+ try
+ {
+ stmt = classMapping.createResourceQueryStatement(accessor, folderID, name, exactMatch, context);
+ resultSet = stmt.executeQuery();
+
+ while (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Resource query returned ID " + id); //$NON-NLS-1$
+ }
+
+ if (!context.addResource(id))
+ {
+ // No more results allowed
+ return false; // don't continue
+ }
+ }
+
+ return true; // continue with other results
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ private CDOID getMinLocalID(Connection connection)
+ {
+ IIDHandler idHandler = getStore().getIDHandler();
+ CDOID min = idHandler.getMaxCDOID();
+
+ // Do not call getClassMappings() at this point, as the package registry is not yet initialized!
+ String dbName = getStore().getRepository().getName();
+ List<String> names = DBUtil.getAllTableNames(connection, dbName);
+
+ String prefix = "SELECT MIN(t." + CDODBSchema.ATTRIBUTES_ID + ") FROM " + dbName + "." + CDODBSchema.CDO_OBJECTS
+ + " AS o, " + dbName + ".";
+
+ String suffix = " AS t WHERE t." + CDODBSchema.ATTRIBUTES_BRANCH + "<0 AND t." + CDODBSchema.ATTRIBUTES_ID + "=o."
+ + CDODBSchema.ATTRIBUTES_ID + " AND t." + CDODBSchema.ATTRIBUTES_CREATED + "=o."
+ + CDODBSchema.ATTRIBUTES_CREATED;
+
+ for (String name : names)
+ {
+ Statement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ stmt = connection.createStatement();
+ resultSet = stmt.executeQuery(prefix + name + suffix);
+
+ if (resultSet.next())
+ {
+ CDOID id = idHandler.getCDOID(resultSet, 1);
+ if (id != null && idHandler.compare(id, min) < 0)
+ {
+ min = id;
+ }
+ }
+ }
+ catch (SQLException ex)
+ {
+ //$FALL-THROUGH$
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmt);
+ }
+ }
+
+ return min;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java
new file mode 100644
index 0000000000..9ca17b94ad
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java
@@ -0,0 +1,485 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 271444: [DB] Multiple refactorings
+ * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class AbstractListTableMapping extends BasicAbstractListTableMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListTableMapping.class);
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The type mapping for the value field.
+ */
+ private ITypeMapping typeMapping;
+
+ // --------- SQL strings - see initSQLStrings() -----------------
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ private String sqlInsertEntry;
+
+ public AbstractListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initTable();
+ initSQLStrings();
+ }
+
+ private void initTable()
+ {
+ IMappingStrategy mappingStrategy = getMappingStrategy();
+ String tableName = mappingStrategy.getTableName(getContainingClass(), getFeature());
+ table = mappingStrategy.getStore().getDBSchema().addTable(tableName);
+
+ // add fields for keys (cdo_id, version, feature_id)
+ FieldInfo[] fields = getKeyFields();
+ IDBField[] dbFields = new IDBField[fields.length + 1];
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType());
+ }
+
+ // add field for list index
+ dbFields[dbFields.length - 1] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
+
+ // add field for value
+ typeMapping = mappingStrategy.createValueMapping(getFeature());
+ typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
+
+ // add table indexes
+ table.addIndex(Type.UNIQUE, dbFields);
+ }
+
+ protected abstract FieldInfo[] getKeyFields();
+
+ protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException;
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ private void initSQLStrings()
+ {
+ String tableName = getTable().getName();
+ FieldInfo[] fields = getKeyFields();
+
+ // ---------------- SELECT to read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ if (i + 1 < fields.length)
+ {
+ // more to come
+ builder.append("=? AND "); //$NON-NLS-1$
+ }
+ else
+ {
+ // last one
+ builder.append("=? "); //$NON-NLS-1$
+ }
+ }
+
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$
+
+ // ----------------- INSERT - reference entry -----------------
+ builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append("("); //$NON-NLS-1$
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ builder.append(", "); //$NON-NLS-1$
+ }
+
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(") VALUES ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append("?, "); //$NON-NLS-1$
+ }
+
+ builder.append(" ?, ?)"); //$NON-NLS-1$
+ sqlInsertEntry = builder.toString();
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final ITypeMapping getTypeMapping()
+ {
+ return typeMapping;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ MoveableList<Object> list = revision.getList(getFeature());
+
+ if (listChunk == 0 || list.size() == 0)
+ {
+ // nothing to read take shortcut
+ return;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision.getID(), revision.getVersion());
+ }
+
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+ setKeyFields(stmt, revision);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace(stmt.toString());
+ }
+
+ if (listChunk != CDORevision.UNCHUNKED)
+ {
+ stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
+ }
+
+ resultSet = stmt.executeQuery();
+
+ int currentIndex = 0;
+ while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
+ {
+ Object value = typeMapping.readValue(resultSet);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$
+ }
+
+ list.set(currentIndex++, value);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision.getID(), revision.getVersion());
+ }
+ }
+
+ public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+
+ IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
+ if (where != null)
+ {
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(where);
+ }
+
+ builder.append(sqlOrderByIndex);
+
+ String sql = builder.toString();
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
+ setKeyFields(stmt, chunkReader.getRevision());
+
+ resultSet = stmt.executeQuery();
+
+ Chunk chunk = null;
+ int chunkSize = 0;
+ int chunkIndex = 0;
+ int indexInChunk = 0;
+
+ while (resultSet.next())
+ {
+ Object value = typeMapping.readValue(resultSet);
+
+ if (chunk == null)
+ {
+ chunk = chunks.get(chunkIndex++);
+ chunkSize = chunk.size();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$
+ chunkSize);
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$
+ }
+
+ chunk.add(indexInChunk++, value);
+ if (indexInChunk == chunkSize)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Chunk finished"); //$NON-NLS-1$
+ }
+
+ chunk = null;
+ indexInChunk = 0;
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ CDOList values = revision.getList(getFeature());
+
+ int idx = 0;
+ for (Object element : values)
+ {
+ writeValue(accessor, revision, idx++, element);
+ }
+ }
+
+ protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
+ {
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", getContainingClass().getName(),
+ getFeature().getName(), idx, revision.getID(), revision.getVersion(), value);
+ }
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
+
+ setKeyFields(stmt, revision);
+ int column = getKeyFields().length + 1;
+ stmt.setInt(column++, idx);
+ typeMapping.setValue(stmt, column++, value);
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
+ QueryXRefsContext context, String idString)
+ {
+ String tableName = getTable().getName();
+ String listJoin = getMappingStrategy().getListJoin("a_t", "l_t");
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT l_t."); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append(", l_t."); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(", l_t."); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" AS l_t, ");//$NON-NLS-1$
+ builder.append(mainTableName);
+ builder.append(" AS a_t WHERE ");//$NON-NLS-1$
+ builder.append("a_t." + mainTableWhere);//$NON-NLS-1$
+ builder.append(listJoin);
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(" IN "); //$NON-NLS-1$
+ builder.append(idString);
+ String sql = builder.toString();
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ ResultSet resultSet = null;
+ Statement stmt = null;
+
+ try
+ {
+ stmt = accessor.getConnection().createStatement();
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Query XRefs (list): {0}", sql);
+ }
+
+ resultSet = stmt.executeQuery(sql);
+ while (resultSet.next())
+ {
+ CDOID srcId = idHandler.getCDOID(resultSet, 1);
+ CDOID targetId = idHandler.getCDOID(resultSet, 2);
+ int idx = resultSet.getInt(3);
+
+ boolean more = context.addXRef(targetId, srcId, (EReference)getFeature(), idx);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", srcId, targetId, idx);
+ }
+
+ if (!more)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" result limit reached. Ignoring further results.");
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(stmt);
+ }
+ }
+
+ /**
+ * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ...
+ *
+ * @author Stefan Winkler
+ */
+ protected static class FieldInfo
+ {
+ private String name;
+
+ private DBType dbType;
+
+ public FieldInfo(String name, DBType dbType)
+ {
+ this.name = name;
+ this.dbType = dbType;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public DBType getDbType()
+ {
+ return dbType;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java
new file mode 100644
index 0000000000..d24e2239c0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractObjectTypeMapper.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 259402
+ * Stefan Winkler - redesign (prepared statements)
+ * Stefan Winkler - bug 276926
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.IObjectTypeMapper;
+
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public abstract class AbstractObjectTypeMapper extends Lifecycle implements IObjectTypeMapper
+{
+ private IMappingStrategy mappingStrategy;
+
+ private IMetaDataManager metaDataManager;
+
+ public AbstractObjectTypeMapper()
+ {
+ }
+
+ public IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public void setMappingStrategy(IMappingStrategy mappingStrategy)
+ {
+ this.mappingStrategy = mappingStrategy;
+ }
+
+ public IMetaDataManager getMetaDataManager()
+ {
+ return metaDataManager;
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ checkState(mappingStrategy, "mappingStrategy"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ metaDataManager = getMappingStrategy().getStore().getMetaDataManager();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ metaDataManager = null;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java
new file mode 100644
index 0000000000..a9c7adce32
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping
+{
+ private FieldInfo[] keyFields;
+
+ public AuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ if (keyFields == null)
+ {
+ keyFields = new FieldInfo[] {
+ new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
+ new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) };
+ }
+
+ return keyFields;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, revision.getVersion());
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ // the audit list mapping does not care about revised references -> NOP
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java
new file mode 100644
index 0000000000..150fc06e63
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java
@@ -0,0 +1,1212 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
+ * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455
+ * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
+ * Stefan Winkler - cleanup, merge and maintenance
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
+import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMap;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This is a featuremap-table mapping for audit mode. It is optimized for frequent insert operations at the list's end,
+ * which causes just 1 DB row to be changed. This is achieved by introducing a version range (columns
+ * {@link CDODBSchema#LIST_REVISION_VERSION_ADDED cdo_version_added} and
+ * {@link CDODBSchema#LIST_REVISION_VERSION_REMOVED cdo_version_removed}) which records for which revisions a particular
+ * entry existed. Also, this mapping is mainly optimized for potentially very large lists: the need for having the
+ * complete list stored in memory to do in-the-middle-moved and inserts is traded in for a few more DB access
+ * operations.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @author Lothar Werzinger
+ * @since 3.0
+ */
+public class AuditFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements
+ IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditFeatureMapTableMappingWithRanges.class);
+
+ /**
+ * Used to clean up lists for detached objects.
+ */
+ private static final int FINAL_VERSION = Integer.MAX_VALUE;
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The tags mapped to column names
+ */
+ private HashMap<CDOID, String> tagMap;
+
+ /**
+ * Column name Set
+ */
+ private List<String> columnNames;
+
+ /**
+ * The type mappings for the value fields.
+ */
+ private Map<CDOID, ITypeMapping> typeMappings;
+
+ private List<DBType> dbTypes;
+
+ // --------- SQL strings - see initSQLStrings() -----------------
+
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ private String sqlInsert;
+
+ private String sqlRemoveEntry;
+
+ private String sqlDeleteEntry;
+
+ private String sqlUpdateIndex;
+
+ private String sqlGetValue;
+
+ private String sqlClearList;
+
+ private String sqlDeleteList;
+
+ public AuditFeatureMapTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass,
+ EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initDBTypes();
+ initTable();
+ initSQLStrings();
+ }
+
+ private void initDBTypes()
+ {
+ // TODO add annotation processing here ...
+ ITypeMapping.Registry registry = ITypeMapping.Registry.INSTANCE;
+ dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes());
+ }
+
+ private void initTable()
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
+ table = store.getDBSchema().addTable(tableName);
+
+ // add fields for CDOID
+ IDBField idField = table.addField(CDODBSchema.FEATUREMAP_REVISION_ID, store.getIDHandler().getDBType());
+
+ // add fields for version range
+ IDBField versionAddedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_ADDED, DBType.INTEGER);
+ IDBField versionRemovedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_REMOVED, DBType.INTEGER);
+
+ // add field for list index
+ IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER);
+
+ // add field for FeatureMap tag (MetaID for Feature in CDO registry)
+ IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType());
+
+ tagMap = new HashMap<CDOID, String>();
+ typeMappings = new HashMap<CDOID, ITypeMapping>();
+ columnNames = new ArrayList<String>();
+
+ // create columns for all DBTypes
+ for (DBType type : getDBTypes())
+ {
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name();
+ table.addField(column, type);
+ columnNames.add(column);
+ }
+
+ // TODO think about indices
+ table.addIndex(Type.NON_UNIQUE, idField);
+ table.addIndex(Type.NON_UNIQUE, versionAddedField);
+ table.addIndex(Type.NON_UNIQUE, versionRemovedField);
+ table.addIndex(Type.NON_UNIQUE, idxField);
+ table.addIndex(Type.NON_UNIQUE, tagField);
+ }
+
+ private void initSQLStrings()
+ {
+ String tableName = getTable().getName();
+
+ // ---------------- SELECT to read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(", "); //$NON-NLS-1$
+
+ Iterator<String> iter = columnNames.iterator();
+ while (iter.hasNext())
+ {
+ builder.append(iter.next());
+ if (iter.hasNext())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ }
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(">?)"); //$NON-NLS-1$
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$
+
+ // ----------------- INSERT - prefix -----------------
+ builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(columnNames.get(i));
+ }
+
+ builder.append(") VALUES (?, ?, ?, ?, ?"); //$NON-NLS-1$
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ builder.append(", ?"); //$NON-NLS-1$
+ }
+
+ builder.append(")"); //$NON-NLS-1$
+ sqlInsert = builder.toString();
+
+ // ----------------- remove current entry -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlRemoveEntry = builder.toString();
+
+ // ----------------- delete temporary entry -----------------
+ builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlDeleteEntry = builder.toString();
+
+ // ----------------- update index -----------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=? WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=?"); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+
+ // ----------------- get current value -----------------
+ builder = new StringBuilder("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(", "); //$NON-NLS-1$
+
+ iter = columnNames.iterator();
+ while (iter.hasNext())
+ {
+ builder.append(iter.next());
+ if (iter.hasNext())
+ {
+ builder.append(", "); //$NON-NLS-1$
+ }
+ }
+
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlGetValue = builder.toString();
+
+ // ----------- clear list items -------------------------
+ builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlClearList = builder.toString();
+
+ // ----------- delete temporary list items -------------------------
+ builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
+ builder.append(" IS NULL"); //$NON-NLS-1$
+ sqlDeleteList = builder.toString();
+ }
+
+ protected List<DBType> getDBTypes()
+ {
+ return dbTypes;
+ }
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final List<String> getColumnNames()
+ {
+ return columnNames;
+ }
+
+ protected final Map<CDOID, ITypeMapping> getTypeMappings()
+ {
+ return typeMappings;
+ }
+
+ protected final Map<CDOID, String> getTagMap()
+ {
+ return tagMap;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ MoveableList<Object> list = revision.getList(getFeature());
+
+ if (listChunk == 0 || list.size() == 0)
+ {
+ // nothing to read take shortcut
+ return;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature() //$NON-NLS-1$
+ .getName(), revision.getID(), revision.getVersion());
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
+
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
+
+ idHandler.setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, revision.getVersion());
+ stmt.setInt(3, revision.getVersion());
+
+ if (listChunk != CDORevision.UNCHUNKED)
+ {
+ stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
+ }
+
+ resultSet = stmt.executeQuery();
+
+ int currentIndex = 0;
+ while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
+ {
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$
+ }
+
+ list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), revision.getID(), revision.getVersion());
+ }
+ }
+
+ private void addFeature(CDOID tag)
+ {
+ EStructuralFeature modelFeature = getFeatureByTag(tag);
+
+ ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature);
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$
+
+ tagMap.put(tag, column);
+ typeMapping.setDBField(table, column);
+ typeMappings.put(tag, typeMapping);
+ }
+
+ public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
+ }
+
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
+ PreparedStatement stmt = null;
+ ResultSet resultSet = null;
+
+ try
+ {
+ StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
+ if (where != null)
+ {
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(where);
+ }
+
+ builder.append(sqlOrderByIndex);
+
+ String sql = builder.toString();
+ stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
+ idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID());
+ stmt.setInt(2, chunkReader.getRevision().getVersion());
+ stmt.setInt(3, chunkReader.getRevision().getVersion());
+
+ resultSet = stmt.executeQuery();
+
+ Chunk chunk = null;
+ int chunkSize = 0;
+ int chunkIndex = 0;
+ int indexInChunk = 0;
+
+ while (resultSet.next())
+ {
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+
+ if (chunk == null)
+ {
+ chunk = chunks.get(chunkIndex++);
+ chunkSize = chunk.size();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$
+ chunkSize);
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$
+ }
+
+ chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
+ if (indexInChunk == chunkSize)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Chunk finished"); //$NON-NLS-1$
+ }
+
+ chunk = null;
+ indexInChunk = 0;
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
+ getFeature(), chunkReader.getRevision());
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
+ {
+ CDOList values = revision.getList(getFeature());
+
+ int idx = 0;
+ for (Object element : values)
+ {
+ writeValue(accessor, revision, idx++, element);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing done"); //$NON-NLS-1$
+ }
+ }
+
+ protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER
+ .format(
+ "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, revision.getID(), revision.getVersion(), idx, value, revision.getTimeStamp());
+ }
+
+ /**
+ * Get column name (lazy).
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the column name where the values are stored
+ */
+ protected String getColumnName(CDOID tag)
+ {
+ String column = tagMap.get(tag);
+ if (column == null)
+ {
+ addFeature(tag);
+ column = tagMap.get(tag);
+ }
+
+ return column;
+ }
+
+ /**
+ * Get type mapping (lazy).
+ *
+ * @param tag
+ * The feature's MetaID in CDO
+ * @return the corresponding type mapping
+ */
+ protected ITypeMapping getTypeMapping(CDOID tag)
+ {
+ ITypeMapping typeMapping = typeMappings.get(tag);
+ if (typeMapping == null)
+ {
+ addFeature(tag);
+ typeMapping = typeMappings.get(tag);
+ }
+
+ return typeMapping;
+ }
+
+ /**
+ * @param metaID
+ * @return the column name where the values are stored
+ */
+ private EStructuralFeature getFeatureByTag(CDOID tag)
+ {
+ return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag);
+ }
+
+ /**
+ * @param feature
+ * The EStructuralFeature
+ * @return The feature's MetaID in CDO
+ */
+ protected CDOID getTagByFeature(EStructuralFeature feature, long timestamp)
+ {
+ return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timestamp);
+ }
+
+ /**
+ * Clear a list of a given revision.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision from which to remove all items
+ */
+ public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmtDeleteTemp = null;
+ PreparedStatement stmtClear = null;
+
+ try
+ {
+ // delete temporary entries
+ stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH);
+ idHandler.setCDOID(stmtDeleteTemp, 1, id);
+ stmtDeleteTemp.setInt(2, newVersion);
+
+ int result = DBUtil.update(stmtDeleteTemp, false);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$
+ }
+
+ // clear rest of the list
+ stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
+ stmtClear.setInt(1, newVersion);
+ idHandler.setCDOID(stmtClear, 2, id);
+
+ result = DBUtil.update(stmtClear, false);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmtDeleteTemp);
+ statementCache.releasePreparedStatement(stmtClear);
+ }
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$
+ }
+
+ CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch();
+
+ // get revision from cache to find out version number
+ CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager()
+ .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
+
+ // set cdo_revision_removed for all list items (so we have no NULL values)
+ clearList(accessor, id, revision.getVersion(), FINAL_VERSION);
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
+ final int newVersion, long created, CDOListFeatureDelta delta)
+ {
+ IRepository repo = accessor.getStore().getRepository();
+ InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id,
+ repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
+
+ int oldListSize = originalRevision.getList(getFeature()).size();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
+ oldListSize);
+ }
+
+ // let the visitor collect the changes
+ ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion, created);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Processing deltas..."); //$NON-NLS-1$
+ }
+
+ for (CDOFeatureDelta listDelta : delta.getListChanges())
+ {
+ listDelta.accept(visitor);
+ }
+ }
+
+ private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ private IDBStoreAccessor accessor;
+
+ private InternalCDORevision originalRevision;
+
+ private CDOID id;
+
+ private int oldVersion;
+
+ private int newVersion;
+
+ private int lastIndex;
+
+ private long timestamp;
+
+ public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion,
+ int newVersion, long timestamp)
+ {
+ this.accessor = accessor;
+ this.originalRevision = originalRevision;
+ id = this.originalRevision.getID();
+ this.oldVersion = oldVersion;
+ this.newVersion = newVersion;
+ lastIndex = originalRevision.getList(getFeature()).size() - 1;
+ this.timestamp = timestamp;
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ int fromIdx = delta.getOldPosition();
+ int toIdx = delta.getNewPosition();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$
+ }
+
+ Object value = getValue(accessor, id, fromIdx);
+
+ // remove the item
+ removeEntry(accessor, id, oldVersion, newVersion, fromIdx);
+
+ // adjust indexes and shift either up or down
+ if (fromIdx < toIdx)
+ {
+ moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx);
+ }
+ else
+ { // fromIdx > toIdx here
+ moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1);
+ }
+
+ // create the item
+ addEntry(accessor, id, newVersion, toIdx, value, timestamp);
+ }
+
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ int startIndex = delta.getIndex();
+ int endIndex = lastIndex;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$
+ }
+
+ if (startIndex <= endIndex)
+ {
+ // make room for the new item
+ moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex);
+ }
+
+ // create the item
+ addEntry(accessor, id, newVersion, startIndex, delta.getValue(), timestamp);
+
+ ++lastIndex;
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ int startIndex = delta.getIndex();
+ int endIndex = lastIndex;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$
+ }
+
+ // remove the item
+ removeEntry(accessor, id, oldVersion, newVersion, startIndex);
+
+ // make room for the new item
+ moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex);
+
+ --lastIndex;
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ int index = delta.getIndex();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$
+ }
+
+ // remove the item
+ removeEntry(accessor, id, oldVersion, newVersion, index);
+
+ // create the item
+ addEntry(accessor, id, newVersion, index, delta.getValue(), timestamp);
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ if (delta.getFeature().isUnsettable())
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Unsetting"); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id, oldVersion, newVersion);
+ lastIndex = -1;
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Delta Clearing"); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id, oldVersion, newVersion);
+ lastIndex = -1;
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ throw new ImplementationError("Should not be called"); //$NON-NLS-1$
+ }
+
+ private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
+ int endIndex)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+
+ for (int index = startIndex; index <= endIndex; ++index)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$
+ }
+
+ int column = 1;
+ stmt.setInt(column++, index - 1);
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 0:
+ Object value = getValue(accessor, id, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, oldVersion, newVersion, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, id, newVersion, index - 1, value, timestamp);
+ break;
+
+ case 1:
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
+ }
+
+ break;
+
+ default:
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
+ }
+
+ throw new DBException("Too many results"); //$NON-NLS-1$
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
+ int endIndex)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ for (int index = endIndex; index >= startIndex; --index)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$
+ }
+
+ int column = 1;
+ stmt.setInt(column++, index + 1);
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, newVersion);
+ stmt.setInt(column++, index);
+
+ int result = DBUtil.update(stmt, false);
+ switch (result)
+ {
+ case 0:
+ Object value = getValue(accessor, id, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
+ }
+
+ removeEntry(accessor, id, oldVersion, newVersion, index);
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$
+ }
+
+ addEntry(accessor, id, newVersion, index + 1, value, timestamp);
+ break;
+
+ case 1:
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
+ }
+
+ break;
+
+ default:
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
+ }
+
+ throw new DBException("Too many results"); //$NON-NLS-1$
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+ }
+
+ private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value, long timestamp)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, version, value);
+ }
+
+ try
+ {
+ FeatureMap.Entry entry = (FeatureMap.Entry)value;
+ EStructuralFeature entryFeature = entry.getEStructuralFeature();
+ CDOID tag = getTagByFeature(entryFeature, timestamp);
+ String columnName = getColumnName(tag);
+
+ stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, version);
+ stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved
+ stmt.setInt(column++, index);
+ idHandler.setCDOID(stmt, column++, tag);
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ if (columnNames.get(i).equals(columnName))
+ {
+ getTypeMapping(tag).setValue(stmt, column++, entry.getValue());
+ }
+ else
+ {
+ stmt.setNull(column++, getDBTypes().get(i).getCode());
+ }
+ }
+
+ DBUtil.update(stmt, true);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ catch (IllegalStateException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, newVersion);
+ }
+
+ try
+ {
+ // try to delete a temporary entry first
+ stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, index);
+ stmt.setInt(column++, newVersion);
+
+ int result = DBUtil.update(stmt, false);
+ if (result == 1)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$
+ }
+ }
+ else if (result > 1)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$
+ }
+
+ throw new DBException("Too many results"); //$NON-NLS-1$
+ }
+ else
+ {
+ // no temporary entry found, so mark the entry as removed
+ statementCache.releasePreparedStatement(stmt);
+ stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH);
+
+ column = 1;
+ stmt.setInt(column++, newVersion);
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, index);
+ DBUtil.update(stmt, true);
+ }
+ }
+ catch (SQLException e)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
+ }
+
+ throw new DBException(e);
+ }
+ catch (IllegalStateException e)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
+ getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
+ }
+
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+ }
+
+ private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int index)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ IPreparedStatementCache statementCache = accessor.getStatementCache();
+ PreparedStatement stmt = null;
+ FeatureMap.Entry result = null;
+
+ try
+ {
+ stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH);
+
+ int column = 1;
+ idHandler.setCDOID(stmt, column++, id);
+ stmt.setInt(column++, index);
+
+ ResultSet resultSet = stmt.executeQuery();
+ if (!resultSet.next())
+ {
+ throw new DBException("getValue expects exactly one result");
+ }
+
+ CDOID tag = idHandler.getCDOID(resultSet, 1);
+ Object value = getTypeMapping(tag).readValue(resultSet);
+ result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ statementCache.releasePreparedStatement(stmt);
+ }
+
+ return result;
+ }
+
+ public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
+ QueryXRefsContext context, String idString)
+ {
+ // must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported
+ // here)
+ throw new ImplementationError("Should never be called!");
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java
new file mode 100644
index 0000000000..9918525de5
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * This is a list-table mapping for audit mode. It has ID and version columns and no delta support.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class AuditListTableMapping extends AbstractListTableMapping
+{
+ private FieldInfo[] keyFields;
+
+ public AuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ if (keyFields == null)
+ {
+ keyFields = new FieldInfo[] {
+ new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
+ new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) };
+ }
+
+ return keyFields;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
+ stmt.setInt(2, revision.getVersion());
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ // the audit list mapping does not care about revised references -> NOP
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java
new file mode 100644
index 0000000000..a3f0b83b24
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java
@@ -0,0 +1,1084 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * This class has been derived from AbstractListTableMapping
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
+ * Stefan Winkler - cleanup, merge and maintenance
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
+import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.server.IRepository;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This is a list-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, which
+ * causes just 1 DB row to be changed. This is achieved by introducing a version range (columns cdo_version_added and
+ * cdo_version_removed) which records for which revisions a particular entry existed. Also, this mapping is mainly
+ * optimized for potentially very large lists: the need for having the complete list stored in memopy to do
+ * in-the-middle-moved and inserts is traded in for a few more DB access operations.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @author Lothar Werzinger
+ */
+public class AuditListTableMappingWithRanges extends BasicAbstractListTableMapping implements IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditListTableMappingWithRanges.class);
+
+ /**
+ * Used to clean up lists for detached objects.
+ */
+ private static final int FINAL_VERSION = Integer.MAX_VALUE;
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The type mapping for the value field.
+ */
+ private ITypeMapping typeMapping;
+
+ // --------- SQL strings - see initSQLStrings() -----------------
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ private String sqlInsertEntry;
+
+ private String sqlDeleteEntry;
+
+ private String sqlRemoveEntry;
+
+ private String sqlUpdateIndex;
+
+ private String sqlGetValue;
+
+ private String sqlClearList;
+
+ private String sqlDeleteList;
+
+ public AuditListTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initTable();
+ initSQLStrings();
+ }
+
+ private void initTable()
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
+ table = store.getDBSchema().addTable(tableName);
+
+ IDBField[] dbFields = new IDBField[4];
+
+ dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType());
+ dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER);
+ dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER);
+ dbFields[3] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
+
+ // add field for value
+ typeMapping = getMappingStrategy().createValueMapping(getFeature());
+ typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
+
+ // TODO think about indexes
+ // add table indexes
+ table.addIndex(Type.UNIQUE, dbFields);
+ }
+
+ public Collection<IDBTable> getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ private void initSQLStrings()
+ {
+ String tableName = getTable().getName();
+
+ // ---------------- read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(" FROM "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
+ builder.append("<=? AND ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(" IS NULL OR "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
+ builder.append(">?)"); //$NON-NLS-1$
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$
+
+ // ----------------- insert entry -----------------
+ builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
+ builder.append(tableName);
+ builder.append("("); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ build